Skip to content

Sprint 8c: slug implementation#165

Merged
frigini merged 24 commits intomasterfrom
feature/sprint-8c-provider-web
Mar 19, 2026
Merged

Sprint 8c: slug implementation#165
frigini merged 24 commits intomasterfrom
feature/sprint-8c-provider-web

Conversation

@frigini
Copy link
Copy Markdown
Owner

@frigini frigini commented Mar 13, 2026

Summary by CodeRabbit

  • Novas Funcionalidades

    • Suporte completo a slug para provedores: geração, normalização, armazenamento, lookup por id ou slug e busca indexada; endpoint público aceita id ou slug.
    • Novo utilitário público SlugHelper para gerar e normalizar slugs.
  • Correções/Melhorias

    • Slug propagada em DTOs, eventos de domínio, integrações, mapeamentos e índices do banco.
    • Lookup público refatorado para IdOuSlug com cache e fallback GUID→slug; respeito à privacidade de contato.
    • Adição de método de busca por slug no repositório e mensagens de erro centralizadas.
  • Documentação

    • Novo guia para converter designs em componentes React.
  • Testes

    • Cobertura ampliada: geração, normalização, lookup por slug, handlers e casos de integração.

…ities, DTOs, mappers, repositories, event handlers, APIs, and comprehensive tests.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 13, 2026

Warning

Rate limit exceeded

@frigini has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 14 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c00d5a3-d8c9-4aab-bcb2-1e071fdf20bb

📥 Commits

Reviewing files that changed from the base of the PR and between f5199f2 and c26cd53.

📒 Files selected for processing (1)
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
📝 Walkthrough

Walkthrough

Adiciona suporte a slugs em toda a stack: utilitário SlugHelper; Slug em entidades (Provider, SearchableProvider), DTOs, domain/integration events; persistência (coluna + índice) e repositórios com GetBySlugAsync; endpoint público agora aceita ID ou slug; diversos handlers, mapeamentos, DI e testes atualizados; inclui novo documento de design React.

Changes

Cohort / File(s) Summary
Utilitário & testes
src/Shared/Utilities/SlugHelper.cs, tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs
Novo SlugHelper (Generate / GenerateWithSuffix) e testes cobrindo normalização e casos-limite.
Providers — Domínio & eventos
src/Modules/Providers/Domain/Entities/Provider.cs, src/Modules/Providers/Domain/Events/ProviderRegisteredDomainEvent.cs, src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs
Adicionada propriedade imutável Slug na entidade e incluída em eventos de domínio (registro/atualização).
Providers — Persistência & repositório
src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs, src/Modules/Providers/Infrastructure/Persistence/Repositories/ProviderRepository.cs, src/Modules/Providers/Domain/Repositories/IProviderRepository.cs
Mapeamento da coluna slug (required, max 120) e índice; interface e repositório ganharam GetBySlugAsync.
Providers — API, DTOs, queries & handlers
src/Contracts/.../Modules/Providers/DTOs/*, src/Modules/Providers/Application/DTOs/*, src/Modules/Providers/Application/Mappers/ProviderMapper.cs, src/Modules/Providers/API/Endpoints/Public/GetPublicProviderByIdOrSlugEndpoint.cs, src/Modules/Providers/Application/Queries/GetPublicProviderByIdOrSlugQuery.cs, src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs, src/Modules/Providers/Application/Extensions.cs
Inclui Slug em DTOs e mapeamentos; substitui query/endpoint por variante IdOrSlug; handler tenta GUID → fallback por slug; DI atualizado para novo handler.
SearchProviders — Domínio, persistência & API
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs, src/Modules/SearchProviders/Application/DTOs/SearchableProviderDto.cs, src/Modules/SearchProviders/Infrastructure/Persistence/..., src/Modules/SearchProviders/Application/ModuleApi/SearchProvidersModuleApi.cs, src/Modules/SearchProviders/Application/Handlers/SearchProvidersQueryHandler.cs
Adiciona Slug na entidade e DTOs de busca; Create/Reconstitute/UpdateBasicInfo aceitam e normalizam slug; persistência e projeções atualizadas.
Mensageria & mappers
src/Shared/Messaging/Messages/Providers/*, src/Modules/Providers/Infrastructure/Events/Mappers/ProviderEventMappers.cs
Integration events agora aceitam/propagam Slug (opcional); mapeadores atualizados para incluir slug.
Endpoints / constantes de rota
src/Shared/Utilities/Constants/ApiEndpoints.cs, src/Modules/Providers/API/Endpoints/ProvidersModuleEndpoints.cs
Rota pública renomeada para /public/{idOrSlug}; constante e endpoint atualizados para GetPublicByIdOrSlug.
Tests — ajustes e novos casos
múltiplos arquivos em src/Modules/Providers/Tests/..., src/Modules/SearchProviders/Tests/..., tests/MeAjudaAi.Web.Admin.Tests/...
Adaptações extensas: inclusão de Slug em fixtures/DTOs, uso de argumentos nomeados, novos testes para lookup por slug e validações de slug; ajustes de fixtures e assertions.
Infra, namespaces e ajustes menores
src/Modules/SearchProviders/Infrastructure/Extensions.cs, src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/*, src/Modules/Users/*, src/Shared/Database/DatabaseConstants.cs
Ajustes em tratamento de connection string/testing; renomeio de namespace Middleware → Middlewares; pragmas para supressões S2068/S3267; correções de nullability (!) e pequenas refatorações.
Documentação
prompts/design-react-project.md
Novo guia de design React adicionado (documentação).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Cliente
    participant Endpoint as GetPublicProviderByIdOrSlugEndpoint
    participant Handler as GetPublicProviderByIdOrSlugQueryHandler
    participant Repo as ProviderRepository
    participant DB as Database

    Client->>Endpoint: GET /public/{idOrSlug}
    activate Endpoint
    Endpoint->>Handler: HandleAsync(GetPublicProviderByIdOrSlugQuery)
    deactivate Endpoint

    activate Handler
    alt idOrSlug é GUID válido
        Handler->>Repo: GetByIdAsync(providerId)
    else fallback / slug
        Handler->>Repo: GetBySlugAsync(slug)
    end
    deactivate Handler

    activate Repo
    Repo->>DB: SELECT ... FROM providers WHERE id=@id OR slug=@slug
    DB-->>Repo: provider row
    Repo-->>Handler: Provider entity
    deactivate Repo

    activate Handler
    Handler->>Handler: Map Provider -> PublicProviderDto (inclui Slug)
    Handler-->>Endpoint: Result<PublicProviderDto?>
    deactivate Handler

    activate Endpoint
    Endpoint-->>Client: 200 OK / 404 NotFound
    deactivate Endpoint
Loading

Estimated code review effort

🎯 4 (Complexo) | ⏱️ ~60 minutos

Possibly related PRs

Poem

🐇 Eu sou o coelho que vira slug,
Troco acento, espaço e deixo tudo snug,
Providers agora têm rota e indexação,
Por id ou por nome, acham sua posição,
Pulo contente — viva a nova versão! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning A descrição do PR está vazia. O template requer seções como Sumário, Problema, Solução, Mudanças, Impacto, Testes e Checklist. Preencha a descrição do PR usando o template fornecido, incluindo sumário, problema resolvido, solução implementada, mudanças principais, impacto esperado, testes realizados e checklist de validação.
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed O título 'Sprint 8c: slug implementation' é direto, claro e descreve bem a mudança principal: implementação de slug para provedores.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/sprint-8c-provider-web
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (1)

147-170: ⚠️ Potential issue | 🟡 Minor

Normalize o Slug também na reconstituição.

Reconstitute ainda copia o valor bruto do banco, então dados legados com espaços ou casing diferente quebram a mesma invariável garantida por Create e UpdateBasicInfo.

🛠️ Ajuste sugerido
     internal static SearchableProvider Reconstitute(
         Guid id,
         Guid providerId,
         string name,
         string slug,
         GeoPoint location,
         ESubscriptionTier subscriptionTier,
         decimal averageRating,
         int totalReviews,
         Guid[] serviceIds,
         bool isActive,
         string? description = null,
         string? city = null,
         string? state = null)
     {
+        if (string.IsNullOrWhiteSpace(slug))
+        {
+            throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));
+        }
+
         var searchableProvider = new SearchableProvider(
             new SearchableProviderId(id),
             providerId,
             name,
             location,
             subscriptionTier)
         {
-            Slug = slug,
+            Slug = slug.Trim().ToLowerInvariant(),
             Description = description,
             City = city,
             State = state,
             AverageRating = averageRating,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 147 - 170, No método Reconstitute da classe SearchableProvider, normalize
o valor de Slug antes de atribuí-lo para manter a mesma invariante aplicada em
Create e UpdateBasicInfo; localize SearchableProvider.Reconstitute e substituir
a atribuição Slug = slug por uma atribuição que aplica a mesma rotina de
normalização usada por Create/UpdateBasicInfo (por exemplo o helper/método de
slugificação existente — nome similar a NormalizeSlug/Slugify — ou reimplemente
a mesma lógica de trim, lowercase e remoção de espaços/caracteres), garantindo
que dados legados sejam normalizados ao reconstituir a entidade.
🧹 Nitpick comments (14)
src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs (1)

188-197: Prefira SlugHelper.Generate no teste para evitar divergência de regra.

Hoje o teste duplica a lógica de slug manualmente; isso pode mascarar regressões quando a regra oficial mudar.

Refactor sugerido
+using MeAjudaAi.Shared.Utilities;
...
 var providerName = _faker.Company.CompanyName();
 var provider = SearchableProvider.Create(
     providerId: providerId,
     name: providerName,
-    slug: providerName.ToLower().Replace(" ", "-"),
+    slug: SlugHelper.Generate(providerName),
     location: location,
     subscriptionTier: _faker.Random.Enum<ESubscriptionTier>(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs`
around lines 188 - 197, O teste está replicando a lógica de geração de slug
manualmente; em vez de usar providerName.ToLower().Replace(" ", "-") passe o
slug gerado por SlugHelper.Generate(providerName) ao criar o objeto via
SearchableProvider.Create (referenciar providerName e o parâmetro slug) para
garantir que o teste acompanhe a regra oficial de geração de slugs.
prompts/design-react-project.md (1)

138-152: Adicione especificador de linguagem ao bloco de código.

O bloco de código para variáveis CSS não possui especificador de linguagem, o que dispara um aviso do markdownlint. Adicione css ou text para melhorar a formatação e silenciar o warning.

📝 Sugestão de correção
-```
+```css
 bg-surface, bg-surface-raised       → fundos
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@prompts/design-react-project.md` around lines 138 - 152, The code block
containing the CSS variable mappings (lines with identifiers like bg-surface,
bg-primary, bg-destructive, text-foreground, border-border, ring-ring, etc.)
lacks a language specifier; update the fenced code block start to include a
language (e.g., ```css or ```text) so markdownlint warning is silenced and
formatting is improved—locate the block showing the mappings and change the
opening fence to ```css.
tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs (1)

6-7: Considere adicionar o atributo de categoria para consistência.

Os outros arquivos de teste no projeto utilizam [Trait("Category", "Unit")] para categorização. Adicionar este atributo manteria a consistência e facilitaria a filtragem de testes por categoria.

♻️ Sugestão de refatoração
+[Trait("Category", "Unit")]
 public class SlugHelperTests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs` around lines
6 - 7, A classe de teste SlugHelperTests está faltando a categorização
consistente usada no projeto; adicione o atributo [Trait("Category", "Unit")]
acima da declaração da classe SlugHelperTests (ou em cada método de teste se
preferir granularidade) para alinhar com os outros testes; verifique que o
namespace/xUnit esteja disponível (usando Xunit) e mantenha o mesmo padrão de
categoria usado pelo restante do repositório.
src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs (1)

44-47: Padronize o limite de tamanho do Slug entre Providers e SearchProviders.

Aqui está 200, mas no módulo Providers está 120. Vale alinhar (ou extrair constante compartilhada) para evitar drift de schema/regra de domínio.

Based on learnings: prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs`
around lines 44 - 47, In SearchableProviderConfiguration (builder.Property(p =>
p.Slug)) the Slug max length is set to 200 but Providers uses 120; change
HasMaxLength(200) to the shared canonical value (preferably use the constant
from Shared.Contracts, e.g. import and reference the Slug length constant) or
set it to 120 to match Providers, and update any related EF migration/schema to
keep database and domain rules aligned.
src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs (1)

70-71: Boa atualização dos testes para o novo construtor, mas falta validar o payload do Slug.

Como este PR introduz Slug no evento de domínio, vale reforçar o Verify para checar o conteúdo publicado (não só o tipo), assim que o contrato de integração expuser esse campo.

Also applies to: 96-97, 123-124

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs`
around lines 70 - 71, Atualize os asserts/verificações nos testes em
ProviderRegisteredDomainEventHandlerTests para não só checar o tipo do evento
publicado, mas também validar o payload.Slug; especificamente, ao verificar a
chamada de Publish/Send (o Verify usando o mock do bus ou publisher), confirme
que o evento publicado (ProviderRegisteredDomainEvent) tem a propriedade Slug
igual a "provider-test". Aplique a mesma verificação nas outras ocorrências
apontadas (asserções nas seções correspondentes às linhas 96-97 e 123-124) para
garantir que todos os testes validem o conteúdo do Slug além do tipo do evento.
src/Modules/Providers/Application/Mappers/ProviderMapper.cs (1)

17-36: Mistura inconsistente de parâmetros nomeados e posicionais.

O método começa com parâmetros nomeados (linhas 18-22: Id, UserId, Name, Slug, Type) mas depois muda para parâmetros posicionais (linhas 23-35). Isso reduz a legibilidade e aumenta o risco de erros se a ordem dos parâmetros do construtor mudar.

♻️ Usar parâmetros nomeados consistentemente
     public static ProviderDto ToDto(this Provider provider)
     {
         return new ProviderDto(
             Id: provider.Id.Value,
             UserId: provider.UserId,
             Name: provider.Name,
             Slug: provider.Slug,
             Type: provider.Type,
-            provider.BusinessProfile.ToDto(),
-            provider.Status,
-            provider.VerificationStatus,
-            provider.Tier,
-            provider.Documents.Select(d => d.ToDto()).ToList(),
-            provider.Qualifications.Select(q => q.ToDto()).ToList(),
-            provider.Services.Select(s => s.ToDto()).ToList(),
-            provider.CreatedAt,
-            provider.UpdatedAt,
-            provider.IsDeleted,
-            provider.DeletedAt,
-            provider.SuspensionReason,
-            provider.RejectionReason
+            BusinessProfile: provider.BusinessProfile.ToDto(),
+            Status: provider.Status,
+            VerificationStatus: provider.VerificationStatus,
+            Tier: provider.Tier,
+            Documents: provider.Documents.Select(d => d.ToDto()).ToList(),
+            Qualifications: provider.Qualifications.Select(q => q.ToDto()).ToList(),
+            Services: provider.Services.Select(s => s.ToDto()).ToList(),
+            CreatedAt: provider.CreatedAt,
+            UpdatedAt: provider.UpdatedAt,
+            IsDeleted: provider.IsDeleted,
+            DeletedAt: provider.DeletedAt,
+            SuspensionReason: provider.SuspensionReason,
+            RejectionReason: provider.RejectionReason
         );
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Providers/Application/Mappers/ProviderMapper.cs` around lines 17
- 36, O trecho que instancia ProviderDto mistura argumentos nomeados e
posicionais; abraçar consistência e segurança: atualize a chamada em
ProviderMapper para usar apenas parâmetros nomeados (por exemplo Id:, UserId:,
Name:, Slug:, Type:, BusinessProfile:, Status:, VerificationStatus:, Tier:,
Documents:, Qualifications:, Services:, CreatedAt:, UpdatedAt:, IsDeleted:,
DeletedAt:, SuspensionReason:, RejectionReason:) referenciando as propriedades
atuais (p.ex. provider.BusinessProfile.ToDto(),
provider.Documents.Select(...).ToList(), etc.), garantindo que cada valor
corresponda explicitamente ao parâmetro do construtor ProviderDto.
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs (2)

164-172: Derivação de slug inconsistente com a produção.

Assim como no arquivo de testes unitários do repositório, a geração de slug aqui usa name.ToLower().Replace(" ", "-") (linha 167), que não corresponde ao comportamento de SlugHelper.Generate(). Em testes de integração isso é ainda mais crítico, pois os dados persistidos no banco podem ter slugs que nunca seriam gerados em produção.

♻️ Usar SlugHelper.Generate para consistência
         var provider = SearchableProvider.Create(
             providerId: providerId,
             name: name,
-            slug: name.ToLower().Replace(" ", "-"),
+            slug: SlugHelper.Generate(name),
             location: location,
             subscriptionTier: tier,
             description: description,
             city: city,
             state: state);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
around lines 164 - 172, A geração de slug em SearchableProvider.Create está
usando name.ToLower().Replace(" ", "-"), o que difere do comportamento de
produção; substitua essa expressão pelo uso de SlugHelper.Generate(name) ao
construir o argumento slug (na chamada de SearchableProvider.Create) e
importe/ajuste o namespace se necessário para garantir que os slugs em testes de
integração sejam gerados exatamente como em produção.

192-200: Mesma inconsistência de slug no segundo helper.

O método CreateTestSearchableProviderWithProviderId também usa a derivação naive de slug na linha 195.

♻️ Usar SlugHelper.Generate para consistência
         var provider = SearchableProvider.Create(
             providerId: providerId,
             name: name,
-            slug: name.ToLower().Replace(" ", "-"),
+            slug: SlugHelper.Generate(name),
             location: location,
             subscriptionTier: tier,
             description: description,
             city: city,
             state: state);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
around lines 192 - 200, The CreateTestSearchableProviderWithProviderId helper
currently derives the slug naively using name.ToLower().Replace(" ", "-");
update this to use the centralized SlugHelper.Generate method for consistency —
modify the provider creation in CreateTestSearchableProviderWithProviderId (the
SearchableProvider.Create(...) call that sets slug) to call
SlugHelper.Generate(name) instead of the manual replace so slugs match other
helpers.
src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs (1)

53-67: Derivação de slug inconsistente com a produção.

A geração de slug na linha 55 usa actualName.ToLower().Replace(" ", "-"), que difere significativamente do SlugHelper.Generate() usado em produção. O helper de produção também:

  1. Remove diacríticos (ex: "café" → "cafe")
  2. Remove caracteres não-alfanuméricos (ex: "&" é removido)
  3. Colapsa hífens duplicados
  4. Faz trim de hífens nas extremidades

Isso pode criar dados de teste que nunca existiriam em produção, potencialmente mascarando bugs ou causando falsos positivos.

♻️ Usar SlugHelper.Generate para consistência
-        var actualSlug = actualName.ToLower().Replace(" ", "-");
+        var actualSlug = SlugHelper.Generate(actualName);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs`
around lines 53 - 67, The test builds a slug with actualName.ToLower().Replace("
", "-") which diverges from production slug behavior; update the test to
generate slugs using the same production helper (call
SlugHelper.Generate(actualName) when constructing the SearchableProvider in
SearchableProvider.Create) so diacritics, non-alphanumerics, duplicate hyphens
and edge hyphens are handled consistently with production (ensure any required
using/import for SlugHelper is added).
src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs (1)

43-46: Documentação XML deve estar em Português.

As tags <summary> estão em inglês, mas de acordo com a política de linguagem do projeto, comentários e documentação XML devem estar em Português.

📝 Traduzir documentação XML para Português
-    /// <summary>
-    /// Verifies that HandleAsync publishes a ProviderProfileUpdatedIntegrationEvent with correct provider details
-    /// when processing a valid profile update domain event.
-    /// </summary>
+    /// <summary>
+    /// Verifica que HandleAsync publica um ProviderProfileUpdatedIntegrationEvent com os detalhes corretos do provedor
+    /// ao processar um evento de domínio de atualização de perfil válido.
+    /// </summary>
-    /// <summary>
-    /// Verifies that when message bus publishing fails, the handler logs an error containing
-    /// "Error handling ProviderProfileUpdatedDomainEvent" and re-throws the exception.
-    /// </summary>
+    /// <summary>
+    /// Verifica que quando a publicação no message bus falha, o handler registra um erro contendo
+    /// "Error handling ProviderProfileUpdatedDomainEvent" e relança a exceção.
+    /// </summary>

Baseado em learnings: "Comments and XML documentation can remain in Portuguese, logs should be in English".

Also applies to: 82-85

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs`
around lines 43 - 46, A documentação XML nas tags <summary> para o teste que
descreve a verificação de HandleAsync deve ser traduzida para Português;
localize no arquivo ProviderProfileUpdatedDomainEventHandlerTests (as
ocorrências atuais nas linhas próximas aos trechos que descrevem "Verifies that
HandleAsync publishes a ProviderProfileUpdatedIntegrationEvent..." e a segunda
ocorrência perto das linhas 82-85) e substitua o texto em inglês por um resumo
em Português mantendo as mesmas tags XML /// <summary> ... </summary>, por
exemplo descrevendo que o teste verifica se HandleAsync publica um
ProviderProfileUpdatedIntegrationEvent com os detalhes corretos do provider;
preserve a formatação XML e outros comentários adjacentes.
src/Shared/Utilities/SlugHelper.cs (1)

26-48: Implementação sólida com uma consideração de edge case.

A implementação está bem estruturada usando GeneratedRegex para performance. Um edge case a considerar: se o texto de entrada contiver apenas caracteres especiais (ex: "!@#$%"), o método retornará uma string vazia, o que causará ArgumentException em SearchableProvider.Create() e UpdateBasicInfo().

Isso é comportamento defensivo adequado, mas pode ser útil documentar ou validar isso no ponto de entrada (ex: validação de nome do provider antes de gerar o slug).

📝 Sugestão: Adicionar documentação sobre o edge case
     /// <summary>
     /// Gera um slug a partir de um texto
     /// </summary>
     /// <param name="text">Texto original</param>
-    /// <returns>Slug formatado</returns>
+    /// <returns>Slug formatado ou string vazia se o texto não contiver caracteres alfanuméricos válidos</returns>
+    /// <remarks>
+    /// Se o texto contiver apenas caracteres especiais, o método retornará string vazia.
+    /// Chamadores devem validar o resultado antes de usar em contextos que requerem slug não-vazio.
+    /// </remarks>
     public static string Generate(string text)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Utilities/SlugHelper.cs` around lines 26 - 48, The Generate method
can return an empty string for inputs with only non-alphanumeric chars (e.g.
"!@#$%"), which later causes an ArgumentException in SearchableProvider.Create
and UpdateBasicInfo; add defensive validation at the call sites: in
SearchableProvider.Create and UpdateBasicInfo, validate the provider name (not
null/whitespace) before calling SlugHelper.Generate, then call Generate and if
it returns null/empty throw a clear ArgumentException (or return a meaningful
default/validation error) that explains the name produced no valid slug;
alternatively, if you prefer centralizing the check, modify SlugHelper.Generate
to detect the empty-result case and either throw a descriptive ArgumentException
or return a documented fallback value, and update callers to handle that
behavior accordingly.
src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs (1)

357-362: Prefira gerar o Slug com a mesma utility da produção.

Os fixtures aqui misturam slug hardcoded e ToLower().Replace(" ", "-"), o que pode divergir do SlugHelper em casos de acento, pontuação ou múltiplos espaços. Vale centralizar isso para os testes continuarem refletindo a regra real.

♻️ Exemplo
+using MeAjudaAi.Shared.Utilities;
...
             var name = $"Provider {i + 1}";
+            var slug = SlugHelper.Generate(name);
             providers.Add(SearchableProvider.Create(
                 providerId: Guid.NewGuid(),
                 name: name,
-                slug: name.ToLower().Replace(" ", "-"),
+                slug: slug,
                 location: new GeoPoint(-23.5505 + i * 0.01, -46.6333 + i * 0.01),

Also applies to: 396-405

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs`
around lines 357 - 362, Os fixtures criam slugs manualmente, o que pode divergir
da lógica de produção; update os testes para gerar o slug usando a mesma utility
que a aplicação usa (ex.: SlugHelper or whatever slug generator is used) ao
construir o SearchableProvider via SearchableProvider.Create so that
providerId/name/slug use the canonical slugging logic; apply the same change to
the other fixture block referenced (around the 396-405 area) so tests mirror
production behavior for accents, punctuation and multiple spaces.
src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs (1)

26-34: Faltam cenários para a nova invariável de Slug.

A suíte foi adaptada para passar slug, mas ainda não cobre os novos guards de Create/UpdateBasicInfo nem a normalização para lowercase. Um teste com slug vazio/branco e outro verificando o valor normalizado protegeriam melhor a mudança.

Also applies to: 58-63, 73-77, 90-111

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`
around lines 26 - 34, Os testes faltam cenários cobrindo a nova invariável slug:
adicione casos unitários em SearchableProviderTests que verifiquem (1) que
Create(...) e UpdateBasicInfo(...) falham (lançam/retornam erro de validação)
quando slug é null/empty/whitespace e (2) que quando um slug válido com letras
maiúsculas é passado, o valor armazenado em SearchableProvider.Slug é
normalizado para lowercase; foque nas chamadas SearchableProvider.Create(...) e
no método UpdateBasicInfo(...) para inserir asserts que confirmem as guards e a
normalização.
src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs (1)

158-222: Os testes ainda não validam a propagação do Slug.

Os fixtures agora populam Slug, mas as asserções só conferem contagem/chamada ao repositório. Se o mapping da API devolver ou persistir o slug errado, esses cenários continuam verdes. Vale validar o Slug no resultado de busca e nas entidades enviadas para AddAsync/UpdateAsync.

♻️ Exemplo
         provider.ProviderId.Should().Be(providerId);
         provider.Name.Should().Be("Provider 1");
+        provider.Slug.Should().Be("provider-1");
...
-        _repositoryMock.Verify(x => x.AddAsync(It.IsAny<SearchableProvider>(), It.IsAny<CancellationToken>()), Times.Once);
+        _repositoryMock.Verify(x => x.AddAsync(
+            It.Is<SearchableProvider>(p => p.Slug == "test-provider"),
+            It.IsAny<CancellationToken>()), Times.Once);
...
-        _repositoryMock.Verify(x => x.UpdateAsync(existingProvider, It.IsAny<CancellationToken>()), Times.Once);
+        _repositoryMock.Verify(x => x.UpdateAsync(
+            It.Is<SearchableProvider>(p => p.Slug == "updated-provider"),
+            It.IsAny<CancellationToken>()), Times.Once);

Also applies to: 327-357, 375-405, 468-509

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs`
around lines 158 - 222, The tests currently don't assert that the provider Slug
is propagated; update the unit tests that exercise SearchProvidersAsync (and the
other search/add/update tests mentioned) to assert provider.Slug equals the
expected "provider-1" in the returned ModuleSearchableProviderDto (from
PagedResult<SearchableProviderDto>) and add mock verifications that repository
methods (e.g., AddAsync/UpdateAsync) are called with entities whose Slug
property equals the expected slug; locate assertions around
result.Value.Items[0] (variable provider) and the mock setups on
_queryDispatcherMock and repository mocks to add these Slug checks so mapping
and persistence of Slug are validated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Providers/Domain/Entities/Provider.cs`:
- Around line 139-140: O campo Slug está sendo gerado apenas a partir do nome
(Slug = SlugHelper.Generate(Name)) o que causa colisões com o índice único em
providers.slug; altere a lógica para garantir slug único antes de persistir:
crie/usar um método como EnsureUniqueSlugAsync/GenerateUniqueSlug que tenta
SlugHelper.Generate(Name) e, enquanto ProviderRepository.SlugExists(slug,
excludeId) for true, anexe um sufixo incremental ("-1", "-2", ...) ou um hash
curto até obter um slug livre, então atribua Slug ao entity; alternativamente,
ao invés de só confiar no banco, capture conflito de unique constraint durante
Save/Update e reexecute a geração/atualização do slug e re-persista; aplique
mesma correção nas outras ocorrências mencionadas (blocos em 174-189 e 211-241)
referenciando as mesmas funções e o repositório de providers.

In `@src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs`:
- Around line 12-22: The domain event ProviderProfileUpdatedDomainEvent now
includes Slug but the integration event ProviderProfileUpdatedIntegrationEvent
and the mapper ProviderEventMappers.ToIntegrationEvent() were not updated; add a
nullable Slug property to ProviderProfileUpdatedIntegrationEvent (e.g., string?
Slug = null) and update ProviderEventMappers.ToIntegrationEvent to set Slug:
domainEvent.Slug when constructing the integration event so consumers like
SearchProvidersModuleApi receive slug updates when "Slug" appears in
UpdatedFields.

In `@src/Modules/Providers/Domain/Events/ProviderRegisteredDomainEvent.cs`:
- Around line 27-28: O evento de domínio ProviderRegisteredDomainEvent agora
inclui o campo Slug, mas ele não está sendo propagado: atualize o contrato de
integração ProviderRegisteredIntegrationEvent para expor uma propriedade Slug
(mesmo tipo que no domínio) e ajuste o mapeador em ProviderEventMappers (a
função que transforma ProviderRegisteredDomainEvent em
ProviderRegisteredIntegrationEvent) para atribuir integrationEvent.Slug =
domainEvent.Slug; garanta também que construtores/serialização do
ProviderRegisteredIntegrationEvent aceitem/definam o Slug para evitar perda de
dado ao publicar.

In
`@src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs`:
- Around line 250-252: The unique index declared by ProviderConfiguration via
builder.HasIndex(p => p.Slug).IsUnique() will cause persistence errors for
providers with duplicate names; remove the uniqueness constraint (drop
.IsUnique()) or replace it with a non-unique index, or alternatively make the
index composite (e.g., include a tenant or owner key) and/or update the slug
generation logic to append a disambiguating suffix so Slug values are unique;
locate the HasIndex(p => p.Slug) call in ProviderConfiguration and apply one of
these fixes consistently with existing slug generation code.

---

Outside diff comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 147-170: No método Reconstitute da classe SearchableProvider,
normalize o valor de Slug antes de atribuí-lo para manter a mesma invariante
aplicada em Create e UpdateBasicInfo; localize SearchableProvider.Reconstitute e
substituir a atribuição Slug = slug por uma atribuição que aplica a mesma rotina
de normalização usada por Create/UpdateBasicInfo (por exemplo o helper/método de
slugificação existente — nome similar a NormalizeSlug/Slugify — ou reimplemente
a mesma lógica de trim, lowercase e remoção de espaços/caracteres), garantindo
que dados legados sejam normalizados ao reconstituir a entidade.

---

Nitpick comments:
In `@prompts/design-react-project.md`:
- Around line 138-152: The code block containing the CSS variable mappings
(lines with identifiers like bg-surface, bg-primary, bg-destructive,
text-foreground, border-border, ring-ring, etc.) lacks a language specifier;
update the fenced code block start to include a language (e.g., ```css or
```text) so markdownlint warning is silenced and formatting is improved—locate
the block showing the mappings and change the opening fence to ```css.

In `@src/Modules/Providers/Application/Mappers/ProviderMapper.cs`:
- Around line 17-36: O trecho que instancia ProviderDto mistura argumentos
nomeados e posicionais; abraçar consistência e segurança: atualize a chamada em
ProviderMapper para usar apenas parâmetros nomeados (por exemplo Id:, UserId:,
Name:, Slug:, Type:, BusinessProfile:, Status:, VerificationStatus:, Tier:,
Documents:, Qualifications:, Services:, CreatedAt:, UpdatedAt:, IsDeleted:,
DeletedAt:, SuspensionReason:, RejectionReason:) referenciando as propriedades
atuais (p.ex. provider.BusinessProfile.ToDto(),
provider.Documents.Select(...).ToList(), etc.), garantindo que cada valor
corresponda explicitamente ao parâmetro do construtor ProviderDto.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs`:
- Around line 43-46: A documentação XML nas tags <summary> para o teste que
descreve a verificação de HandleAsync deve ser traduzida para Português;
localize no arquivo ProviderProfileUpdatedDomainEventHandlerTests (as
ocorrências atuais nas linhas próximas aos trechos que descrevem "Verifies that
HandleAsync publishes a ProviderProfileUpdatedIntegrationEvent..." e a segunda
ocorrência perto das linhas 82-85) e substitua o texto em inglês por um resumo
em Português mantendo as mesmas tags XML /// <summary> ... </summary>, por
exemplo descrevendo que o teste verifica se HandleAsync publica um
ProviderProfileUpdatedIntegrationEvent com os detalhes corretos do provider;
preserve a formatação XML e outros comentários adjacentes.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs`:
- Around line 70-71: Atualize os asserts/verificações nos testes em
ProviderRegisteredDomainEventHandlerTests para não só checar o tipo do evento
publicado, mas também validar o payload.Slug; especificamente, ao verificar a
chamada de Publish/Send (o Verify usando o mock do bus ou publisher), confirme
que o evento publicado (ProviderRegisteredDomainEvent) tem a propriedade Slug
igual a "provider-test". Aplique a mesma verificação nas outras ocorrências
apontadas (asserções nas seções correspondentes às linhas 96-97 e 123-124) para
garantir que todos os testes validem o conteúdo do Slug além do tipo do evento.

In
`@src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs`:
- Around line 44-47: In SearchableProviderConfiguration (builder.Property(p =>
p.Slug)) the Slug max length is set to 200 but Providers uses 120; change
HasMaxLength(200) to the shared canonical value (preferably use the constant
from Shared.Contracts, e.g. import and reference the Slug length constant) or
set it to 120 to match Providers, and update any related EF migration/schema to
keep database and domain rules aligned.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Around line 164-172: A geração de slug em SearchableProvider.Create está
usando name.ToLower().Replace(" ", "-"), o que difere do comportamento de
produção; substitua essa expressão pelo uso de SlugHelper.Generate(name) ao
construir o argumento slug (na chamada de SearchableProvider.Create) e
importe/ajuste o namespace se necessário para garantir que os slugs em testes de
integração sejam gerados exatamente como em produção.
- Around line 192-200: The CreateTestSearchableProviderWithProviderId helper
currently derives the slug naively using name.ToLower().Replace(" ", "-");
update this to use the centralized SlugHelper.Generate method for consistency —
modify the provider creation in CreateTestSearchableProviderWithProviderId (the
SearchableProvider.Create(...) call that sets slug) to call
SlugHelper.Generate(name) instead of the manual replace so slugs match other
helpers.

In
`@src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs`:
- Around line 357-362: Os fixtures criam slugs manualmente, o que pode divergir
da lógica de produção; update os testes para gerar o slug usando a mesma utility
que a aplicação usa (ex.: SlugHelper or whatever slug generator is used) ao
construir o SearchableProvider via SearchableProvider.Create so that
providerId/name/slug use the canonical slugging logic; apply the same change to
the other fixture block referenced (around the 396-405 area) so tests mirror
production behavior for accents, punctuation and multiple spaces.

In
`@src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs`:
- Around line 158-222: The tests currently don't assert that the provider Slug
is propagated; update the unit tests that exercise SearchProvidersAsync (and the
other search/add/update tests mentioned) to assert provider.Slug equals the
expected "provider-1" in the returned ModuleSearchableProviderDto (from
PagedResult<SearchableProviderDto>) and add mock verifications that repository
methods (e.g., AddAsync/UpdateAsync) are called with entities whose Slug
property equals the expected slug; locate assertions around
result.Value.Items[0] (variable provider) and the mock setups on
_queryDispatcherMock and repository mocks to add these Slug checks so mapping
and persistence of Slug are validated.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`:
- Around line 26-34: Os testes faltam cenários cobrindo a nova invariável slug:
adicione casos unitários em SearchableProviderTests que verifiquem (1) que
Create(...) e UpdateBasicInfo(...) falham (lançam/retornam erro de validação)
quando slug é null/empty/whitespace e (2) que quando um slug válido com letras
maiúsculas é passado, o valor armazenado em SearchableProvider.Slug é
normalizado para lowercase; foque nas chamadas SearchableProvider.Create(...) e
no método UpdateBasicInfo(...) para inserir asserts que confirmem as guards e a
normalização.

In `@src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs`:
- Around line 188-197: O teste está replicando a lógica de geração de slug
manualmente; em vez de usar providerName.ToLower().Replace(" ", "-") passe o
slug gerado por SlugHelper.Generate(providerName) ao criar o objeto via
SearchableProvider.Create (referenciar providerName e o parâmetro slug) para
garantir que o teste acompanhe a regra oficial de geração de slugs.

In
`@src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs`:
- Around line 53-67: The test builds a slug with actualName.ToLower().Replace("
", "-") which diverges from production slug behavior; update the test to
generate slugs using the same production helper (call
SlugHelper.Generate(actualName) when constructing the SearchableProvider in
SearchableProvider.Create) so diacritics, non-alphanumerics, duplicate hyphens
and edge hyphens are handled consistently with production (ensure any required
using/import for SlugHelper is added).

In `@src/Shared/Utilities/SlugHelper.cs`:
- Around line 26-48: The Generate method can return an empty string for inputs
with only non-alphanumeric chars (e.g. "!@#$%"), which later causes an
ArgumentException in SearchableProvider.Create and UpdateBasicInfo; add
defensive validation at the call sites: in SearchableProvider.Create and
UpdateBasicInfo, validate the provider name (not null/whitespace) before calling
SlugHelper.Generate, then call Generate and if it returns null/empty throw a
clear ArgumentException (or return a meaningful default/validation error) that
explains the name produced no valid slug; alternatively, if you prefer
centralizing the check, modify SlugHelper.Generate to detect the empty-result
case and either throw a descriptive ArgumentException or return a documented
fallback value, and update callers to handle that behavior accordingly.

In `@tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs`:
- Around line 6-7: A classe de teste SlugHelperTests está faltando a
categorização consistente usada no projeto; adicione o atributo
[Trait("Category", "Unit")] acima da declaração da classe SlugHelperTests (ou em
cada método de teste se preferir granularidade) para alinhar com os outros
testes; verifique que o namespace/xUnit esteja disponível (usando Xunit) e
mantenha o mesmo padrão de categoria usado pelo restante do repositório.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ca172342-9452-418d-9afe-182a55ada172

📥 Commits

Reviewing files that changed from the base of the PR and between c137cdf and 0a1f6a3.

⛔ Files ignored due to path filters (2)
  • src/Web/MeAjudaAi.Web.Admin/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Web.Admin.Tests/packages.lock.json is excluded by !**/packages.lock.json
📒 Files selected for processing (39)
  • prompts/design-react-project.md
  • src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderBasicDto.cs
  • src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderDto.cs
  • src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderIndexingDto.cs
  • src/Modules/Providers/Application/DTOs/ProviderDto.cs
  • src/Modules/Providers/Application/DTOs/PublicProviderDto.cs
  • src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdQueryHandler.cs
  • src/Modules/Providers/Application/Mappers/ProviderMapper.cs
  • src/Modules/Providers/Application/ModuleApi/ProvidersModuleApi.cs
  • src/Modules/Providers/Domain/Entities/Provider.cs
  • src/Modules/Providers/Domain/Events/ProviderProfileUpdatedDomainEvent.cs
  • src/Modules/Providers/Domain/Events/ProviderRegisteredDomainEvent.cs
  • src/Modules/Providers/Domain/Repositories/IProviderRepository.cs
  • src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs
  • src/Modules/Providers/Infrastructure/Persistence/Repositories/ProviderRepository.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/GetMyProviderProfileEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/GetMyProviderStatusEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/UpdateMyProviderProfileEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/API/Endpoints/UploadMyDocumentEndpointTests.cs
  • src/Modules/Providers/Tests/Unit/Application/Services/ProvidersModuleApiTests.cs
  • src/Modules/Providers/Tests/Unit/Domain/Entities/ProviderTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs
  • src/Modules/SearchProviders/Application/DTOs/SearchableProviderDto.cs
  • src/Modules/SearchProviders/Application/Handlers/SearchProvidersQueryHandler.cs
  • src/Modules/SearchProviders/Application/ModuleApi/SearchProvidersModuleApi.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/DTOs/ProviderSearchResultDto.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Repositories/SearchableProviderRepository.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs
  • src/Shared/Utilities/SlugHelper.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs
  • tests/MeAjudaAi.Web.Admin.Tests/Pages/ProvidersPageTests.cs

Comment thread src/Modules/Providers/Domain/Entities/Provider.cs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (2)

115-129: ⚠️ Potential issue | 🟠 Major

Trim().ToLowerInvariant() ainda aceita slugs inválidos para URL.

Só baixar caixa não torna o valor URL-safe. Entradas como "João Silva / 24h" continuam com espaços, acentos e separadores inválidos, então basta um caller enviar um “slug” parcialmente normalizado para persistirmos rotas quebradas. Centralizem a normalização/validação com a mesma regra compartilhada do projeto em Create e UpdateBasicInfo.

🛠️ Ajuste sugerido
-        if (string.IsNullOrWhiteSpace(slug))
-        {
-            throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));
-        }
+        slug = NormalizeSlug(slug);
...
-            Slug = slug.Trim().ToLowerInvariant(),
+            Slug = slug,
...
-        if (string.IsNullOrWhiteSpace(slug))
-        {
-            throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));
-        }
+        slug = NormalizeSlug(slug);
...
-        Slug = slug.Trim().ToLowerInvariant();
+        Slug = slug;
private static string NormalizeSlug(string slug)
{
    if (string.IsNullOrWhiteSpace(slug))
        throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));

    var normalized = SlugHelper.Generate(slug); // ou validação equivalente
    if (string.IsNullOrWhiteSpace(normalized))
        throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));

    return normalized;
}

Also applies to: 192-199

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 115 - 129, The current slug trimming (Slug =
slug.Trim().ToLowerInvariant()) accepts URL-unsafe values; add a private static
NormalizeSlug(string slug) helper on the SearchableProvider class that
validates, normalizes and returns a URL-safe slug (e.g., via SlugHelper.Generate
or equivalent) and throws ArgumentException for empty/invalid results, then
replace direct Trim/ToLower uses in the factory/constructor call and in
UpdateBasicInfo with NormalizeSlug(slug) so Create and UpdateBasicInfo share the
same normalization/validation logic and ensure Slug remains URL-safe.

147-170: ⚠️ Potential issue | 🟠 Major

Reconstitute mascara dados inválidos com string.Empty.

slug?.Trim().ToLowerInvariant() ?? string.Empty cria uma entidade com Slug == "", embora Create e UpdateBasicInfo proíbam isso. Melhor falhar cedo aqui usando a mesma rotina de normalização/validação; caso contrário, o problema de persistência só aparece depois, na API de busca.

🛠️ Ajuste sugerido
-            Slug = slug?.Trim().ToLowerInvariant() ?? string.Empty,
+            Slug = NormalizeSlug(slug),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 147 - 170, Reconstitute currently sets Slug to
slug?.Trim().ToLowerInvariant() ?? string.Empty which masks invalid data; change
Reconstitute to reuse the same slug normalization/validation used by Create and
UpdateBasicInfo (or call the shared NormalizeSlug method) and if the incoming
slug is null/invalid after normalization throw/return a failure instead of
defaulting to string.Empty so the entity cannot be reconstituted with an empty
Slug; update the assignment in SearchableProvider.Reconstitute (and related
SearchableProvider constructor/Slug setter if needed) to perform normalization
and validation rather than silent fallback.
🧹 Nitpick comments (1)
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs (1)

165-173: Alinhe o fixture com a estratégia real de slug do Provider.

Aqui o teste monta SearchableProvider.Slug com SlugHelper.Generate(name), mas o fluxo real usa SlugHelper.GenerateWithSuffix(Name, Id.Value.ToString("N")[..8]) em src/Modules/Providers/Domain/Entities/Provider.cs. Isso faz a suíte exercitar um formato que não existe em produção e pode esconder bugs de rota/colisão ligados ao sufixo.

♻️ Ajuste sugerido
-            slug: SlugHelper.Generate(name),
+            slug: SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]),

Also applies to: 193-201

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
around lines 165 - 173, O fixture está gerando o slug com
SlugHelper.Generate(name) enquanto o domínio real usa
SlugHelper.GenerateWithSuffix(Name, Id.Value.ToString("N")[..8]) em Provider.cs;
atualize a chamada ao criar o fixture (SearchableProvider.Create) para gerar o
slug com SlugHelper.GenerateWithSuffix passando o nome e o sufixo extraído do
providerId (ou do Id em formato "N" e tomando os 8 primeiros caracteres) para
espelhar a estratégia de produção e evitar discrepâncias de rota/colisão.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Providers/Domain/Entities/Provider.cs`:
- Around line 213-217: O trecho em Provider.cs está regenerando Slug sempre que
o nome muda (veja UpdateProfile / as atribuições a Name e Slug e a chamada
SlugHelper.GenerateWithSuffix(Id.Value.ToString("N")[..8])), o que quebra URLs
públicas; em vez de sobrescrever Slug em renames, mantenha o Slug estável após
criação ou implemente armazenamento de slugs antigos. Conserte removendo a
atribuição automática Slug = SlugHelper.GenerateWithSuffix(...) dentro do fluxo
de atualização (apenas definir Slug na criação quando estiver nulo) ou adicionar
uma coleção/entidade de PreviousSlugs e ao atualizar gerar um novo slug e
persistir o antigo nessa coleção para suportar aliases/redirects; use as
propriedades/métodos Name, Slug, Id e SlugHelper.GenerateWithSuffix como pontos
de alteração.

In
`@src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs`:
- Around line 44-47: A coluna slug foi marcada como obrigatória via
builder.Property(p => p.Slug) mas não existe na migration inicial; crie uma new
migration (não alterar a InitialCreate directly) que: 1) adicione a coluna slug
NULLABLE à tabela searchable_providers; 2) execute um backfill UPDATE (por
exemplo gerando slugs a partir de um campo existente como Name ou Id) para
preencher todos os registros; e 3) altere a coluna para NOT NULL após o
backfill; assegure que o novo migration esteja incluído no conjunto de
migrations aplicadas antes de exigir slug em SearchableProviderConfiguration e
que testes que usam SearchProvidersIntegrationTestBase/EnsureCreatedAsync possam
ser executados localmente com a nova migration.

In `@src/Shared/Utilities/SlugHelper.cs`:
- Around line 56-57: GenerateWithSuffix currently returns
$"{Generate(text)}-{suffix}" which yields "-suffix" if Generate(text) normalizes
to empty; update GenerateWithSuffix to call Generate(text), check if the result
is null or empty, and return just the suffix (or suffix alone without leading
hyphen) when empty, otherwise return the original "{normalized}-{suffix}" form;
reference the GenerateWithSuffix method and the Generate(text) call when making
this change.

---

Outside diff comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 115-129: The current slug trimming (Slug =
slug.Trim().ToLowerInvariant()) accepts URL-unsafe values; add a private static
NormalizeSlug(string slug) helper on the SearchableProvider class that
validates, normalizes and returns a URL-safe slug (e.g., via SlugHelper.Generate
or equivalent) and throws ArgumentException for empty/invalid results, then
replace direct Trim/ToLower uses in the factory/constructor call and in
UpdateBasicInfo with NormalizeSlug(slug) so Create and UpdateBasicInfo share the
same normalization/validation logic and ensure Slug remains URL-safe.
- Around line 147-170: Reconstitute currently sets Slug to
slug?.Trim().ToLowerInvariant() ?? string.Empty which masks invalid data; change
Reconstitute to reuse the same slug normalization/validation used by Create and
UpdateBasicInfo (or call the shared NormalizeSlug method) and if the incoming
slug is null/invalid after normalization throw/return a failure instead of
defaulting to string.Empty so the entity cannot be reconstituted with an empty
Slug; update the assignment in SearchableProvider.Reconstitute (and related
SearchableProvider constructor/Slug setter if needed) to perform normalization
and validation rather than silent fallback.

---

Nitpick comments:
In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Around line 165-173: O fixture está gerando o slug com
SlugHelper.Generate(name) enquanto o domínio real usa
SlugHelper.GenerateWithSuffix(Name, Id.Value.ToString("N")[..8]) em Provider.cs;
atualize a chamada ao criar o fixture (SearchableProvider.Create) para gerar o
slug com SlugHelper.GenerateWithSuffix passando o nome e o sufixo extraído do
providerId (ou do Id em formato "N" e tomando os 8 primeiros caracteres) para
espelhar a estratégia de produção e evitar discrepâncias de rota/colisão.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0030c760-84cd-421b-b8af-6d856dd962b6

📥 Commits

Reviewing files that changed from the base of the PR and between 0a1f6a3 and 4b77f32.

📒 Files selected for processing (20)
  • prompts/design-react-project.md
  • src/Contracts/Contracts/Modules/SearchProviders/DTOs/ModuleSearchableProviderDto.cs
  • src/Modules/Providers/Domain/Entities/Provider.cs
  • src/Modules/Providers/Infrastructure/Events/Mappers/ProviderEventMappers.cs
  • src/Modules/Providers/Tests/Unit/Domain/Entities/ProviderTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderProfileUpdatedDomainEventHandlerTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Events/Handlers/ProviderRegisteredDomainEventHandlerTests.cs
  • src/Modules/SearchProviders/Application/ModuleApi/SearchProvidersModuleApi.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Configurations/SearchableProviderConfiguration.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Modules/SearchProviders/Tests/Unit/Application/Handlers/SearchProvidersQueryHandlerTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Models/SearchResultTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs
  • src/Shared/Messaging/Messages/Providers/ProviderProfileUpdatedIntegrationEvent.cs
  • src/Shared/Messaging/Messages/Providers/ProviderRegisteredIntegrationEvent.cs
  • src/Shared/Utilities/SlugHelper.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Utilities/SlugHelperTests.cs
✅ Files skipped from review due to trivial changes (1)
  • prompts/design-react-project.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Modules/Providers/Tests/Unit/Domain/Entities/ProviderTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Application/ModuleApi/SearchProvidersModuleApiTests.cs
  • src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs

Comment thread src/Modules/Providers/Domain/Entities/Provider.cs Outdated
Comment thread src/Shared/Utilities/SlugHelper.cs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Modules/Providers/Tests/Unit/Infrastructure/Persistence/Configurations/ProviderConfigurationTests.cs (1)

826-836: ⚠️ Potential issue | 🟡 Minor

O nome do método está inconsistente com a asserção.

O método Configure_ShouldHaveSixIndexes agora valida 7 índices, tornando o nome enganoso. Atualize o nome para refletir a nova contagem.

✏️ Correção proposta para o nome do método
 [Fact]
-public void Configure_ShouldHaveSixIndexes()
+public void Configure_ShouldHaveSevenIndexes()
 {
     // Assert - 6 indexes on Provider entity itself:

Também considere atualizar o comentário interno para refletir a nova contagem total:

-    // Assert - 6 indexes on Provider entity itself:
+    // Assert - 7 indexes on Provider entity itself:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Persistence/Configurations/ProviderConfigurationTests.cs`
around lines 826 - 836, Rename the test method Configure_ShouldHaveSixIndexes to
reflect the asserted count (e.g., Configure_ShouldHaveSevenIndexes), and update
the inline comment that lists the expected indexes (the lines enumerating
UserId, Name, Type, Status, VerificationStatus, IsDeleted, Slug) so it matches
the assertion using _entityType.GetIndexes().Should().HaveCount(7); keep the
assertion unchanged.
src/Modules/Providers/API/Endpoints/Public/GetPublicProviderByIdOrSlugEndpoint.cs (1)

24-39: ⚠️ Potential issue | 🟡 Minor

A documentação do endpoint promete Services sempre.

O WithDescription lista “Lista de serviços oferecidos” como retorno fixo, mas o handler correspondente pode devolver Services = [] quando PublicProfilePrivacy estiver ativa. Ajuste o OpenAPI para deixar claro que esse campo é condicional.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/API/Endpoints/Public/GetPublicProviderByIdOrSlugEndpoint.cs`
around lines 24 - 39, The OpenAPI description in
GetPublicProviderByIdOrSlugEndpoint currently promises "Lista de serviços
oferecidos" as always returned; update the endpoint metadata (the
WithDescription string and the response schema definition used by this endpoint)
to indicate that Services is conditional and may be an empty array or omitted
when PublicProfilePrivacy is enabled (or mark the Services property as
optional/nullable in the response DTO schema). Modify the WithDescription text
to mention the privacy condition and adjust the response model/schema used by
the endpoint so Consumers see Services as conditional (e.g., optional or
nullable) instead of always present, referencing
GetPublicProviderByIdOrSlugEndpoint, WithDescription, Services and
PublicProfilePrivacy.
♻️ Duplicate comments (1)
src/Shared/Utilities/SlugHelper.cs (1)

56-60: ⚠️ Potential issue | 🟡 Minor

Sanitize o suffix para evitar slug malformado (base- ou caracteres inválidos).

Hoje o sufixo é anexado sem normalização/validação. Se vier vazio ou com caracteres não amigáveis para URL, o resultado pode ficar inconsistente.

🔧 Ajuste sugerido
 public static string GenerateWithSuffix(string text, string suffix)
 {
     var baseSlug = Generate(text);
-    return string.IsNullOrEmpty(baseSlug) ? suffix : $"{baseSlug}-{suffix}";
+    var normalizedSuffix = Generate(suffix);
+
+    if (string.IsNullOrWhiteSpace(baseSlug))
+        return normalizedSuffix;
+
+    if (string.IsNullOrWhiteSpace(normalizedSuffix))
+        return baseSlug;
+
+    return $"{baseSlug}-{normalizedSuffix}";
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Utilities/SlugHelper.cs` around lines 56 - 60, A função
GenerateWithSuffix está anexando o suffix sem normalização, permitindo
resultados como "base-" ou caracteres inválidos; para corrigir, normalize e
sanitize o suffix antes de concatenar (por exemplo reaproveitando a lógica de
limpeza já existente em Generate: calcular suffixSlug = Generate(suffix.Trim())
), então aplicar estas regras em GenerateWithSuffix: se suffixSlug for vazio
retorne apenas baseSlug; se baseSlug for vazio retorne suffixSlug; caso ambos
existam retorne $"{baseSlug}-{suffixSlug}". Atualize GenerateWithSuffix para
usar Generate para validar/normalizar o suffix e evitar traços finais ou
caracteres inválidos.
🧹 Nitpick comments (3)
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs (1)

193-201: Padronize o segundo helper para também usar GenerateWithSuffix.

Usar só Generate(name) aqui pode gerar colisões em cenários de teste com nomes repetidos.

🔧 Ajuste sugerido
-            slug: SlugHelper.Generate(name),
+            slug: SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
around lines 193 - 201, Alterar o helper usado ao criar o provider para evitar
colisões em testes: na chamada SearchableProvider.Create substitua
SlugHelper.Generate(name) por SlugHelper.GenerateWithSuffix(name) (ou
equivalente overload) para que o slug inclua um sufixo único; atualize qualquer
import/uso relacionado se necessário para compilar e mantenha o restante dos
parâmetros (providerId, name, location, etc.) inalterados.
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (1)

129-130: Centralize a normalização de slug no SlugHelper para manter consistência entre módulos.

NormalizeSlug atual só faz trim + lowercase. Isso diverge da regra canônica usada em Provider e pode persistir slug não padronizado.

♻️ Refactor sugerido
+using MeAjudaAi.Shared.Utilities;
@@
     private static string NormalizeSlug(string slug)
     {
-        var normalized = slug.Trim().ToLowerInvariant();
+        var normalized = SlugHelper.Generate(slug);
         if (string.IsNullOrEmpty(normalized))
             throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));
         return normalized;
     }

Also applies to: 138-144, 177-177, 206-206

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 129 - 130, Replace local NormalizeSlug usage in SearchableProvider with
the canonical slug normalizer from SlugHelper to ensure consistent rules with
Provider; specifically, update assignments and any calls in the
SearchableProvider class (e.g., the Slug property initializer and the other
occurrences noted) to call SlugHelper.NormalizeSlug (the same method used by
Provider) instead of the local NormalizeSlug implementation, remove duplicate
trim/lowercase logic from SearchableProvider, and ensure the class imports/uses
SlugHelper so all slug generation follows the centralized canonical
implementation.
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs (1)

48-48: Falta um teste cobrindo o fluxo novo por slug.

Todos os cenários continuam enviando GUID em formato string e mockando apenas GetByIdAsync, então o branch GetBySlugAsync pode quebrar sem falhar nesta suíte. Vale adicionar pelo menos um caso com provider.Slug.

Also applies to: 73-73, 93-93, 128-128

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`
at line 48, Add a test case that exercises the "by slug" path: instantiate
GetPublicProviderByIdOrSlugQuery with provider.Slug (not provider.Id) and update
the repository mock to setup/verify GetBySlugAsync is called and returns the
provider, not only GetByIdAsync; ensure the handler under test
(GetPublicProviderByIdQueryHandler or the test methods at lines around
48/73/93/128) asserts the same expected result as the existing ID-based tests so
the GetBySlugAsync branch is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Providers/Application/Queries/GetPublicProviderByIdOrSlugQuery.cs`:
- Around line 12-17: The cache key for GetPublicProviderByIdOrSlugQuery
currently only includes IdOrSlug and IsAuthenticated but the handler response
also depends on the provider's PublicProfilePrivacy; update GetCacheKey (on
GetPublicProviderByIdOrSlugQuery) to incorporate the provider's privacy state or
version token (e.g., a privacy enum/value or a profile privacy version/hash) so
cached responses differ when PublicProfilePrivacy changes, or alternatively
disable caching for this query by adjusting GetCacheExpiration to no-cache;
additionally ensure you invalidate or bump the privacy/version used in keys
whenever PublicProfilePrivacy is updated.

In `@src/Shared/Utilities/Constants/ApiEndpoints.cs`:
- Line 52: The public route constant GetPublicByIdOrSlug (value "/{idOrSlug}")
conflicts with the authenticated "/{id:guid}" route under the same providers
group; rename or change the public route to avoid the overlapping pattern (for
example move it under a distinct segment like "/public/{idOrSlug}" or add a
non-ambiguous constraint), then update the endpoint registration that uses
GetPublicByIdOrSlug and any callers to the new constant so the anonymous public
endpoint is in a separate, non-overlapping route (or group) from the
authenticated GetById ("/{id:guid}") handler.

---

Outside diff comments:
In
`@src/Modules/Providers/API/Endpoints/Public/GetPublicProviderByIdOrSlugEndpoint.cs`:
- Around line 24-39: The OpenAPI description in
GetPublicProviderByIdOrSlugEndpoint currently promises "Lista de serviços
oferecidos" as always returned; update the endpoint metadata (the
WithDescription string and the response schema definition used by this endpoint)
to indicate that Services is conditional and may be an empty array or omitted
when PublicProfilePrivacy is enabled (or mark the Services property as
optional/nullable in the response DTO schema). Modify the WithDescription text
to mention the privacy condition and adjust the response model/schema used by
the endpoint so Consumers see Services as conditional (e.g., optional or
nullable) instead of always present, referencing
GetPublicProviderByIdOrSlugEndpoint, WithDescription, Services and
PublicProfilePrivacy.

In
`@src/Modules/Providers/Tests/Unit/Infrastructure/Persistence/Configurations/ProviderConfigurationTests.cs`:
- Around line 826-836: Rename the test method Configure_ShouldHaveSixIndexes to
reflect the asserted count (e.g., Configure_ShouldHaveSevenIndexes), and update
the inline comment that lists the expected indexes (the lines enumerating
UserId, Name, Type, Status, VerificationStatus, IsDeleted, Slug) so it matches
the assertion using _entityType.GetIndexes().Should().HaveCount(7); keep the
assertion unchanged.

---

Duplicate comments:
In `@src/Shared/Utilities/SlugHelper.cs`:
- Around line 56-60: A função GenerateWithSuffix está anexando o suffix sem
normalização, permitindo resultados como "base-" ou caracteres inválidos; para
corrigir, normalize e sanitize o suffix antes de concatenar (por exemplo
reaproveitando a lógica de limpeza já existente em Generate: calcular suffixSlug
= Generate(suffix.Trim()) ), então aplicar estas regras em GenerateWithSuffix:
se suffixSlug for vazio retorne apenas baseSlug; se baseSlug for vazio retorne
suffixSlug; caso ambos existam retorne $"{baseSlug}-{suffixSlug}". Atualize
GenerateWithSuffix para usar Generate para validar/normalizar o suffix e evitar
traços finais ou caracteres inválidos.

---

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`:
- Line 48: Add a test case that exercises the "by slug" path: instantiate
GetPublicProviderByIdOrSlugQuery with provider.Slug (not provider.Id) and update
the repository mock to setup/verify GetBySlugAsync is called and returns the
provider, not only GetByIdAsync; ensure the handler under test
(GetPublicProviderByIdQueryHandler or the test methods at lines around
48/73/93/128) asserts the same expected result as the existing ID-based tests so
the GetBySlugAsync branch is covered.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 129-130: Replace local NormalizeSlug usage in SearchableProvider
with the canonical slug normalizer from SlugHelper to ensure consistent rules
with Provider; specifically, update assignments and any calls in the
SearchableProvider class (e.g., the Slug property initializer and the other
occurrences noted) to call SlugHelper.NormalizeSlug (the same method used by
Provider) instead of the local NormalizeSlug implementation, remove duplicate
trim/lowercase logic from SearchableProvider, and ensure the class imports/uses
SlugHelper so all slug generation follows the centralized canonical
implementation.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Around line 193-201: Alterar o helper usado ao criar o provider para evitar
colisões em testes: na chamada SearchableProvider.Create substitua
SlugHelper.Generate(name) por SlugHelper.GenerateWithSuffix(name) (ou
equivalente overload) para que o slug inclua um sufixo único; atualize qualquer
import/uso relacionado se necessário para compilar e mantenha o restante dos
parâmetros (providerId, name, location, etc.) inalterados.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 865a4b47-deb5-47a3-aff9-25a18529bb15

📥 Commits

Reviewing files that changed from the base of the PR and between 4b77f32 and 9cf226a.

⛔ Files ignored due to path filters (3)
  • src/Modules/SearchProviders/Infrastructure/Migrations/SearchProvidersDbContextModelSnapshot.cs is excluded by !**/Migrations/**
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260317151054_AddSlugToSearchableProvider.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260317151054_AddSlugToSearchableProvider.cs is excluded by !**/Migrations/**
📒 Files selected for processing (18)
  • src/Modules/Providers/API/Endpoints/ProvidersModuleEndpoints.cs
  • src/Modules/Providers/API/Endpoints/Public/GetPublicProviderByIdOrSlugEndpoint.cs
  • src/Modules/Providers/Application/Extensions.cs
  • src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs
  • src/Modules/Providers/Application/Queries/GetPublicProviderByIdOrSlugQuery.cs
  • src/Modules/Providers/Application/Queries/GetPublicProviderByIdQuery.cs
  • src/Modules/Providers/Domain/Entities/Provider.cs
  • src/Modules/Providers/Tests/Infrastructure/TestInfrastructureExtensions.cs
  • src/Modules/Providers/Tests/Integration/GetPublicProviderByIdIntegrationTests.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/GetPublicProviderByIdQueryHandlerTests.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs
  • src/Modules/Providers/Tests/Unit/Domain/Entities/ProviderTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Persistence/Configurations/ProviderConfigurationTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Infrastructure/Extensions.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Shared/Utilities/Constants/ApiEndpoints.cs
  • src/Shared/Utilities/SlugHelper.cs
💤 Files with no reviewable changes (2)
  • src/Modules/Providers/Application/Queries/GetPublicProviderByIdQuery.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/GetPublicProviderByIdQueryHandlerTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Modules/Providers/Tests/Unit/Domain/Entities/ProviderTests.cs

Comment thread src/Shared/Utilities/Constants/ApiEndpoints.cs Outdated
Filipe Frigini added 2 commits March 17, 2026 19:51
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (1)

116-130: ⚠️ Potential issue | 🟠 Major

Valide o slug depois da normalização para preservar invariantes.

Hoje o código valida apenas entrada não vazia; após SlugHelper.Generate(...), o resultado ainda pode ficar vazio (ex.: só caracteres inválidos). Em Reconstitute, slug ?? string.Empty ainda mascara dado inconsistente.

💡 Ajuste sugerido
-        if (string.IsNullOrWhiteSpace(slug))
-        {
-            throw new ArgumentException("Provider slug cannot be empty.", nameof(slug));
-        }
+        if (string.IsNullOrWhiteSpace(slug))
+        {
+            throw new ArgumentException("Slug do provedor não pode ser vazio.", nameof(slug));
+        }

...
-            Slug = SlugHelper.Generate(slug),
+            Slug = NormalizeAndValidateSlug(slug, nameof(slug)),
...
-            Slug = SlugHelper.Generate(slug ?? string.Empty),
+            Slug = NormalizeAndValidateSlug(slug, nameof(slug), isReconstitution: true),
...
-        Slug = SlugHelper.Generate(slug);
+        Slug = NormalizeAndValidateSlug(slug, nameof(slug));
private static string NormalizeAndValidateSlug(string slug, string paramName, bool isReconstitution = false)
{
    var normalized = SlugHelper.Generate(slug);

    if (string.IsNullOrWhiteSpace(normalized))
    {
        throw isReconstitution
            ? new InvalidOperationException("Slug inválido ao reconstituir SearchableProvider.")
            : new ArgumentException("Slug do provedor é inválido.", paramName);
    }

    return normalized;
}

Also applies to: 170-170, 193-199

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 116 - 130, Validate the slug after normalization instead of only checking
the raw input: replace the current pre-check+direct SlugHelper.Generate(...)
assignment with a helper (e.g., NormalizeAndValidateSlug) that calls
SlugHelper.Generate(slug), throws ArgumentException (or
InvalidOperationException when reconstituting) if the normalized result is
null/whitespace, and returns the normalized value; use this helper when creating
SearchableProvider (the Slug property) and in Reconstitute paths to ensure slug
invariants are preserved (refer to SlugHelper.Generate, SearchableProvider
constructor, and Reconstitute).
🧹 Nitpick comments (3)
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs (1)

168-168: Extraia a geração de slug de teste para um helper local.

A expressão SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]) está duplicada; centralizar evita drift se a regra de montagem mudar.

♻️ Refactor opcional
+    private static string BuildTestSlug(string name, Guid providerId) =>
+        SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]);

...
-            slug: SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]),
+            slug: BuildTestSlug(name, providerId),
...
-            slug: SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]),
+            slug: BuildTestSlug(name, providerId),

Also applies to: 196-196

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
at line 168, Extrair a lógica duplicada de criação de slug em
SearchProvidersIntegrationTestBase para um helper local: criar um método privado
(por exemplo GetTestSlug or BuildTestSlug) que recebe name e providerId e
retorna SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]), e
substituir ambas as ocorrências atuais que chamam
SlugHelper.GenerateWithSuffix(...) pelo novo método; referencie o nome da classe
SearchProvidersIntegrationTestBase e a chamada SlugHelper.GenerateWithSuffix
para localizar os pontos a alterar.
src/Shared/Utilities/Constants/ApiEndpoints.cs (1)

52-52: Boa correção da rota; só alinhe o comentário inline.

/public/{idOrSlug} elimina a sobreposição com /{id:guid}, mas o comentário ainda aponta para GetPublicProviderByIdEndpoint. Vale atualizar para o nome atual para não confundir manutenção e buscas no código.

✏️ Ajuste sugerido
-        public const string GetPublicByIdOrSlug = "/public/{idOrSlug}";  // GET GetPublicProviderByIdEndpoint
+        public const string GetPublicByIdOrSlug = "/public/{idOrSlug}";  // GET GetPublicProviderByIdOrSlugEndpoint
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Shared/Utilities/Constants/ApiEndpoints.cs` at line 52, Atualize o
comentário inline que segue a constante GetPublicByIdOrSlug para referir o nome
atual do endpoint (substituir a referência obsoleta
GetPublicProviderByIdEndpoint por o nome real usado no código hoje, por exemplo
GetPublicByIdOrSlugEndpoint ou o identificador atual do handler), mantendo o
formato de comentário existente para evitar confusão em buscas e manutenção;
localize a constante GetPublicByIdOrSlug e altere apenas o texto do comentário
anotado após ela.
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs (1)

61-87: Cubra a propagação do slug no DTO neste teste.

Este caso novo valida o lookup por slug, mas ainda passaria se a busca funcionasse e o mapeamento de Slug no PublicProviderDto quebrasse. Como o slug é parte central deste PR, vale travar essa garantia aqui também.

✅ Ajuste sugerido
         // Assert
         result.IsSuccess.Should().BeTrue();
         result.Value.Should().NotBeNull();
         result.Value!.Id.Should().Be(provider.Id);
+        result.Value.Slug.Should().Be(provider.Slug);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`
around lines 61 - 87, Atualize o teste
HandleAsync_WhenProviderQueriedBySlug_ShouldReturnDto para também verificar que
o slug do DTO é propagado: após chamar _handler.HandleAsync(query, ...), assert
que result.Value (o PublicProviderDto retornado) não é nulo e que
result.Value.Slug é igual a provider.Slug; isso garante que o mapping para
PublicProviderDto inclui o campo Slug (verifique o fluxo envolvendo
GetPublicProviderByIdOrSlugQuery,
_providerRepositoryMock.Setup(...GetBySlugAsync...), e a construção do Provider
via ProviderBuilder).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Line 118: Padronize a mensagem de validação para pt-BR: atualize as exceções
lançadas relacionadas ao parâmetro slug dentro da classe SearchableProvider
(onde atualmente é lançado ArgumentException("Provider slug cannot be empty.",
nameof(slug))) para uma mensagem em português apropriada (por exemplo: "O
identificador do provedor não pode estar vazio.") ou encaminhe essa mensagem
para um mecanismo de tradução na borda; faça o mesmo para a outra ocorrência que
valida slug na mesma classe (referenciada no diff) garantindo que todas as
mensagens user-facing retornadas pela API estejam em pt-BR.

---

Outside diff comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 116-130: Validate the slug after normalization instead of only
checking the raw input: replace the current pre-check+direct
SlugHelper.Generate(...) assignment with a helper (e.g.,
NormalizeAndValidateSlug) that calls SlugHelper.Generate(slug), throws
ArgumentException (or InvalidOperationException when reconstituting) if the
normalized result is null/whitespace, and returns the normalized value; use this
helper when creating SearchableProvider (the Slug property) and in Reconstitute
paths to ensure slug invariants are preserved (refer to SlugHelper.Generate,
SearchableProvider constructor, and Reconstitute).

---

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`:
- Around line 61-87: Atualize o teste
HandleAsync_WhenProviderQueriedBySlug_ShouldReturnDto para também verificar que
o slug do DTO é propagado: após chamar _handler.HandleAsync(query, ...), assert
que result.Value (o PublicProviderDto retornado) não é nulo e que
result.Value.Slug é igual a provider.Slug; isso garante que o mapping para
PublicProviderDto inclui o campo Slug (verifique o fluxo envolvendo
GetPublicProviderByIdOrSlugQuery,
_providerRepositoryMock.Setup(...GetBySlugAsync...), e a construção do Provider
via ProviderBuilder).

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Line 168: Extrair a lógica duplicada de criação de slug em
SearchProvidersIntegrationTestBase para um helper local: criar um método privado
(por exemplo GetTestSlug or BuildTestSlug) que recebe name e providerId e
retorna SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]), e
substituir ambas as ocorrências atuais que chamam
SlugHelper.GenerateWithSuffix(...) pelo novo método; referencie o nome da classe
SearchProvidersIntegrationTestBase e a chamada SlugHelper.GenerateWithSuffix
para localizar os pontos a alterar.

In `@src/Shared/Utilities/Constants/ApiEndpoints.cs`:
- Line 52: Atualize o comentário inline que segue a constante
GetPublicByIdOrSlug para referir o nome atual do endpoint (substituir a
referência obsoleta GetPublicProviderByIdEndpoint por o nome real usado no
código hoje, por exemplo GetPublicByIdOrSlugEndpoint ou o identificador atual do
handler), mantendo o formato de comentário existente para evitar confusão em
buscas e manutenção; localize a constante GetPublicByIdOrSlug e altere apenas o
texto do comentário anotado após ela.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f2329105-56a0-446d-b9cf-7498576233dd

📥 Commits

Reviewing files that changed from the base of the PR and between 9cf226a and 848fda8.

📒 Files selected for processing (9)
  • src/Modules/Providers/API/Endpoints/Public/GetPublicProviderByIdOrSlugEndpoint.cs
  • src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs
  • src/Modules/Providers/Tests/Unit/Infrastructure/Persistence/Configurations/ProviderConfigurationTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Shared/Utilities/Constants/ApiEndpoints.cs
  • src/Shared/Utilities/SlugHelper.cs
  • src/Web/MeAjudaAi.Web.Customer/app/(main)/buscar/page.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Modules/Providers/Tests/Unit/Infrastructure/Persistence/Configurations/ProviderConfigurationTests.cs
  • src/Shared/Utilities/SlugHelper.cs
  • src/Modules/Providers/Infrastructure/Persistence/Configurations/ProviderConfiguration.cs

Comment thread src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs Outdated
…add unit tests for public provider retrieval by ID or slug.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs (1)

21-27: Alinhe o nome da suíte com o comportamento ByIdOrSlug.

A suíte ainda se chama GetPublicProviderByIdQueryHandlerTests, mas agora valida GetPublicProviderByIdOrSlugQueryHandler. Renomear classe/arquivo melhora rastreabilidade e manutenção.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`
around lines 21 - 27, Renomeie a suíte de testes e referências para refletir o
comportamento "ByIdOrSlug": altere a classe de
GetPublicProviderByIdQueryHandlerTests para
GetPublicProviderByIdOrSlugQueryHandlerTests, atualize o nome do ficheiro
correspondente e verifique o construtor e quaisquer referências internas (por
exemplo, o campo _handler já instancia GetPublicProviderByIdOrSlugQueryHandler)
para usar o novo nome da classe; ajuste também quaisquer atributos de test
runner ou namespaces que referenciem o antigo nome.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Around line 166-173: A classe SearchProvidersIntegrationTestBase está chamando
o método BuildTestSlug(string name, string providerId) que não existe; adicione
um método privado ou protected BuildTestSlug na mesma classe que aceita (string
name, string providerId) e retorna uma slug consistente usada pelos testes (por
exemplo: combinar name e providerId, normalizar para minúsculas, substituir
espaços/caracteres inválidos por '-', remover caracteres duplicados e trimar
'-'). Implemente-o na classe base (que implementa IAsyncLifetime) para que as
chamadas BuildTestSlug(name, providerId) nos testes resolvam corretamente e
gerem slugs previsíveis.

---

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`:
- Around line 21-27: Renomeie a suíte de testes e referências para refletir o
comportamento "ByIdOrSlug": altere a classe de
GetPublicProviderByIdQueryHandlerTests para
GetPublicProviderByIdOrSlugQueryHandlerTests, atualize o nome do ficheiro
correspondente e verifique o construtor e quaisquer referências internas (por
exemplo, o campo _handler já instancia GetPublicProviderByIdOrSlugQueryHandler)
para usar o novo nome da classe; ajuste também quaisquer atributos de test
runner ou namespaces que referenciem o antigo nome.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38a638bc-4257-46b4-95f1-6eb336cec240

📥 Commits

Reviewing files that changed from the base of the PR and between 848fda8 and 04b8b9a.

📒 Files selected for processing (4)
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Shared/Utilities/Constants/ApiEndpoints.cs

…nd unit tests for GetPublicProviderByIdOrSlug query handler.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs (1)

166-173: ⚠️ Potential issue | 🔴 Critical

BuildTestSlug está ausente e quebra a compilação do projeto.

As chamadas BuildTestSlug(name, providerId) não resolvem para nenhum método na classe, resultando em CS0103 (linhas reportadas no pipeline). Esse é um bloqueador de merge.

🔧 Correção sugerida
@@
     protected SearchableProvider CreateTestSearchableProviderWithProviderId(
         Guid providerId,
         string name,
         double latitude,
@@
         return provider;
     }
+
+    /// <summary>
+    /// Gera slug determinístico para cenários de teste.
+    /// </summary>
+    private static string BuildTestSlug(string name, Guid providerId)
+    {
+        return SlugHelper.GenerateWithSuffix(name, providerId.ToString("N"));
+    }
 
     /// <summary>
     /// Persiste um SearchableProvider no banco de dados
     /// </summary>

Also applies to: 194-201

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
around lines 166 - 173, The code calls BuildTestSlug(name, providerId) but the
method is missing, causing CS0103; add a private static (or appropriate
visibility) helper method named BuildTestSlug in the
SearchProvidersIntegrationTestBase class that returns a string slug (e.g.,
normalize/slugify the name and include providerId or concatenate them) and use
it for the existing calls at the provider creation sites referenced (the calls
around providerId/name at lines shown and the duplicate calls around the other
block). Ensure the method signature matches the usage (BuildTestSlug(string
name, Guid providerId) or the actual providerId type) and handles null/empty
name safely.
🧹 Nitpick comments (2)
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs (2)

61-88: Adicione o cenário de slug não encontrado.

O caminho novo por slug só está coberto no happy path. Se GetBySlugAsync retornar null, uma regressão nesse branch passa sem proteção de teste.

💡 Exemplo de cenário adicional
[Fact]
public async Task HandleAsync_WhenProviderSlugNotFound_ShouldReturnNotFound()
{
    // Arrange
    const string slug = "provider-inexistente";

    _providerRepositoryMock
        .Setup(x => x.GetBySlugAsync(slug, It.IsAny<CancellationToken>()))
        .ReturnsAsync((Provider?)null);

    var query = new GetPublicProviderByIdOrSlugQuery(slug);

    // Act
    var result = await _handler.HandleAsync(query, CancellationToken.None);

    // Assert
    result.IsSuccess.Should().BeFalse();
    result.Error.StatusCode.Should().Be(404);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`
around lines 61 - 88, Add a unit test for the "slug not found" branch: mock
_providerRepositoryMock.Setup(x => x.GetBySlugAsync(slug,
It.IsAny<CancellationToken>())).ReturnsAsync((Provider?)null), create a
GetPublicProviderByIdOrSlugQuery with that slug, call
_handler.HandleAsync(query, CancellationToken.None) and assert the result
indicates failure (IsSuccess false) and that the error StatusCode is 404; this
covers the branch where GetBySlugAsync returns null and prevents regressions in
HandleAsync.

70-70: Mantenha comentários inline em português.

Os cabeçalhos AAA podem continuar em inglês, mas este comentário novo foge da convenção do repositório.

💬 Ajuste sugerido
-        // Bypass domain transitions to set Active status directly for test
+        // Ignora as transições de domínio para definir o status Active diretamente no teste
Based on learnings: In all C# files, keep logging messages in English, but ensure comments and XML documentation (/// , /// , etc.) are written in Portuguese across the codebase; in unit test code, keep AAA headers in tests (Arrange, Act, Assert) in English and do not translate them.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`
at line 70, Summary: Comentário inline em inglês quebra a convenção do
repositório; substitua por português mantendo cabeçalhos AAA em inglês. Atualize
o comentário "// Bypass domain transitions to set Active status directly for
test" no arquivo de teste GetPublicProviderByIdQueryHandlerTests.cs para uma
versão em português (por exemplo: "// Contornar transições de domínio para
definir o status Active diretamente no teste"), garantindo que os cabeçalhos
Arrange/Act/Assert dentro do mesmo teste permaneçam em inglês.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Around line 166-173: The code calls BuildTestSlug(name, providerId) but the
method is missing, causing CS0103; add a private static (or appropriate
visibility) helper method named BuildTestSlug in the
SearchProvidersIntegrationTestBase class that returns a string slug (e.g.,
normalize/slugify the name and include providerId or concatenate them) and use
it for the existing calls at the provider creation sites referenced (the calls
around providerId/name at lines shown and the duplicate calls around the other
block). Ensure the method signature matches the usage (BuildTestSlug(string
name, Guid providerId) or the actual providerId type) and handles null/empty
name safely.

---

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs`:
- Around line 61-88: Add a unit test for the "slug not found" branch: mock
_providerRepositoryMock.Setup(x => x.GetBySlugAsync(slug,
It.IsAny<CancellationToken>())).ReturnsAsync((Provider?)null), create a
GetPublicProviderByIdOrSlugQuery with that slug, call
_handler.HandleAsync(query, CancellationToken.None) and assert the result
indicates failure (IsSuccess false) and that the error StatusCode is 404; this
covers the branch where GetBySlugAsync returns null and prevents regressions in
HandleAsync.
- Line 70: Summary: Comentário inline em inglês quebra a convenção do
repositório; substitua por português mantendo cabeçalhos AAA em inglês. Atualize
o comentário "// Bypass domain transitions to set Active status directly for
test" no arquivo de teste GetPublicProviderByIdQueryHandlerTests.cs para uma
versão em português (por exemplo: "// Contornar transições de domínio para
definir o status Active diretamente no teste"), garantindo que os cabeçalhos
Arrange/Act/Assert dentro do mesmo teste permaneçam em inglês.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 59493ec1-b3b4-43f3-91a2-172d961959f0

📥 Commits

Reviewing files that changed from the base of the PR and between 848fda8 and 04b8b9a.

📒 Files selected for processing (4)
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Shared/Utilities/Constants/ApiEndpoints.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Shared/Utilities/Constants/ApiEndpoints.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs (1)

282-282: Evite número mágico ao truncar o Guid no sufixo.

Na Line 282, [..8] pode virar constante nomeada para facilitar manutenção futura.

💡 Refactor opcional
+    private const int TestSlugSuffixLength = 8;
+
     private static string BuildTestSlug(string name, Guid providerId)
     {
-        return SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]);
+        return SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..TestSlugSuffixLength]);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`
at line 282, Extrair o literal de truncamento do GUID usado em
SlugHelper.GenerateWithSuffix para uma constante nomeada (por exemplo const int
GuidSuffixLength = 8) e usar essa constante ao invés de usar o slice literal
providerId.ToString("N")[..8]; atualize a chamada para usar
providerId.ToString("N").Substring(0, GuidSuffixLength) ou o equivalente de
slicing com a constante para tornar o propósito claro e facilitar manutenção;
referência: SlugHelper.GenerateWithSuffix e providerId.ToString("N")[..8].
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (2)

70-72: Asserção de null ausente para consistência.

A verificação statusProp.Should().NotBeNull() está presente nos outros testes (linhas 41 e 146), mas foi omitida aqui antes de usar statusProp!.SetValue. Para manter consistência e clareza, considere adicionar a asserção.

♻️ Sugestão de correção
         // Bypass domain transitions to set Active status directly for test
         var statusProp = typeof(Provider).GetProperty(nameof(Provider.Status));
+        statusProp.Should().NotBeNull("Provider.Status property must exist");
         statusProp!.SetValue(provider, EProviderStatus.Active);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 70 - 72, Add a null assertion before using the reflected Status
property: verify statusProp is not null with statusProp.Should().NotBeNull() (as
done in the other tests) immediately after obtaining it from
typeof(Provider).GetProperty(nameof(Provider.Status)) and before calling
statusProp!.SetValue(provider, EProviderStatus.Active); this ensures consistency
and avoids relying on the null-forgiving operator without an assertion.

61-88: Considere adicionar testes para casos de borda.

O teste de busca por slug está correto, mas baseado na lógica do handler (que usa Guid.TryParse para rotear entre ID e slug), há casos de borda importantes não cobertos:

  1. Slug que poderia ser um GUID válido: Se um slug fosse acidentalmente um GUID válido, seria roteado para GetByIdAsync em vez de GetBySlugAsync.

  2. Sensibilidade a maiúsculas/minúsculas: O GetBySlugAsync faz comparação exata (p.Slug == slug), mas os slugs são armazenados normalizados/minúsculos. Uma query com letras maiúsculas falharia.

[Fact]
public async Task HandleAsync_WhenSlugHasUppercase_ShouldStillMatchProvider()
{
    // Arrange
    var provider = ProviderBuilder.Create()
        .WithType(EProviderType.Individual)
        .Build();
    
    var statusProp = typeof(Provider).GetProperty(nameof(Provider.Status));
    statusProp!.SetValue(provider, EProviderStatus.Active);

    // Simula query com maiúsculas, mas slug armazenado é minúsculo
    var uppercaseSlug = provider.Slug.ToUpperInvariant();
    
    _providerRepositoryMock
        .Setup(x => x.GetBySlugAsync(It.IsAny<string>(), It.IsAny<CancellationToken>()))
        .ReturnsAsync((Provider?)null);

    var query = new GetPublicProviderByIdOrSlugQuery(uppercaseSlug);

    // Act
    var result = await _handler.HandleAsync(query, CancellationToken.None);

    // Assert - Documenta o comportamento atual (não encontra)
    // TODO: Decidir se deve normalizar input no handler/repository
    result.IsSuccess.Should().BeFalse();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 61 - 88, Add unit tests and make the handler slug handling
deterministic: add two tests in GetPublicProviderByIdOrSlugQueryHandlerTests —
one that supplies an uppercase slug (e.g., provider.Slug.ToUpperInvariant()) and
expects normalization so GetBySlugAsync matches, and one that supplies a slug
that is a valid GUID string to document/cover routing when Guid.TryParse is
true; then update HandleAsync in GetPublicProviderByIdOrSlugQueryHandler to
normalize the incoming query value (lowercase/trim) before calling
GetBySlugAsync and/or change the routing so it attempts a slug lookup fallback
when GetByIdAsync returns null (use GetByIdAsync and if null call GetBySlugAsync
with the normalized value), and update test mocks to Setup GetBySlugAsync and
GetByIdAsync (and assertions) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 70-72: Add a null assertion before using the reflected Status
property: verify statusProp is not null with statusProp.Should().NotBeNull() (as
done in the other tests) immediately after obtaining it from
typeof(Provider).GetProperty(nameof(Provider.Status)) and before calling
statusProp!.SetValue(provider, EProviderStatus.Active); this ensures consistency
and avoids relying on the null-forgiving operator without an assertion.
- Around line 61-88: Add unit tests and make the handler slug handling
deterministic: add two tests in GetPublicProviderByIdOrSlugQueryHandlerTests —
one that supplies an uppercase slug (e.g., provider.Slug.ToUpperInvariant()) and
expects normalization so GetBySlugAsync matches, and one that supplies a slug
that is a valid GUID string to document/cover routing when Guid.TryParse is
true; then update HandleAsync in GetPublicProviderByIdOrSlugQueryHandler to
normalize the incoming query value (lowercase/trim) before calling
GetBySlugAsync and/or change the routing so it attempts a slug lookup fallback
when GetByIdAsync returns null (use GetByIdAsync and if null call GetBySlugAsync
with the normalized value), and update test mocks to Setup GetBySlugAsync and
GetByIdAsync (and assertions) accordingly.

In
`@src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs`:
- Line 282: Extrair o literal de truncamento do GUID usado em
SlugHelper.GenerateWithSuffix para uma constante nomeada (por exemplo const int
GuidSuffixLength = 8) e usar essa constante ao invés de usar o slice literal
providerId.ToString("N")[..8]; atualize a chamada para usar
providerId.ToString("N").Substring(0, GuidSuffixLength) ou o equivalente de
slicing com a constante para tornar o propósito claro e facilitar manutenção;
referência: SlugHelper.GenerateWithSuffix e providerId.ToString("N")[..8].

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 18ddd6f5-1dd9-4b8e-be48-20dd21690c43

📥 Commits

Reviewing files that changed from the base of the PR and between 04b8b9a and a6285c6.

📒 Files selected for processing (2)
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (1)

61-88: Bom teste para o caminho de busca por slug, mas há duas observações.

  1. Inconsistência na asserção de reflexão: Nas linhas 70-72 falta a asserção statusProp.Should().NotBeNull() que existe nos outros testes (linhas 40-42 e 144-147). Isso torna o teste menos robusto caso a propriedade seja renomeada.

  2. Cobertura de teste incompleta: Não há teste para o cenário "provider não encontrado por slug". O teste HandleAsync_WhenProviderNotFound_ShouldReturnNotFound (linha 113) usa providerId.ToString() que é um GUID válido, então apenas testa o caminho de busca por ID.

♻️ Correção sugerida para a asserção
         // Bypass domain transitions to set Active status directly for test
         var statusProp = typeof(Provider).GetProperty(nameof(Provider.Status));
+        statusProp.Should().NotBeNull("Provider.Status property must exist");
         statusProp!.SetValue(provider, EProviderStatus.Active);
➕ Teste sugerido para slug não encontrado
[Fact]
public async Task HandleAsync_WhenProviderNotFoundBySlug_ShouldReturnNotFound()
{
    // Arrange
    var nonExistentSlug = "provider-inexistente-abc123";

    _providerRepositoryMock
        .Setup(x => x.GetBySlugAsync(nonExistentSlug, It.IsAny<CancellationToken>()))
        .ReturnsAsync((Provider?)null);

    var query = new GetPublicProviderByIdOrSlugQuery(nonExistentSlug);

    // Act
    var result = await _handler.HandleAsync(query, CancellationToken.None);

    // Assert
    result.IsSuccess.Should().BeFalse();
    result.Error.StatusCode.Should().Be(404);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 61 - 88, Add a null-check assertion for the reflected Status
property and add a new unit test for the "not found by slug" path: in
HandleAsync_WhenProviderQueriedBySlug_ShouldReturnDto ensure you assert
statusProp.Should().NotBeNull() after obtaining statusProp via
typeof(Provider).GetProperty(nameof(Provider.Status)) (symbols: Provider,
Provider.Status, statusProp,
HandleAsync_WhenProviderQueriedBySlug_ShouldReturnDto); and add a new test
HandleAsync_WhenProviderNotFoundBySlug_ShouldReturnNotFound that setups
_providerRepositoryMock.Setup(x => x.GetBySlugAsync(nonExistentSlug,
It.IsAny<CancellationToken>())).ReturnsAsync((Provider?)null), invokes new
GetPublicProviderByIdOrSlugQuery(nonExistentSlug) against
_handler.HandleAsync(...), and asserts result.IsSuccess is false and
result.Error.StatusCode equals 404 (symbols: _providerRepositoryMock,
GetBySlugAsync, GetPublicProviderByIdOrSlugQuery,
HandleAsync_WhenProviderNotFoundBySlug_ShouldReturnNotFound).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 61-88: Add a null-check assertion for the reflected Status
property and add a new unit test for the "not found by slug" path: in
HandleAsync_WhenProviderQueriedBySlug_ShouldReturnDto ensure you assert
statusProp.Should().NotBeNull() after obtaining statusProp via
typeof(Provider).GetProperty(nameof(Provider.Status)) (symbols: Provider,
Provider.Status, statusProp,
HandleAsync_WhenProviderQueriedBySlug_ShouldReturnDto); and add a new test
HandleAsync_WhenProviderNotFoundBySlug_ShouldReturnNotFound that setups
_providerRepositoryMock.Setup(x => x.GetBySlugAsync(nonExistentSlug,
It.IsAny<CancellationToken>())).ReturnsAsync((Provider?)null), invokes new
GetPublicProviderByIdOrSlugQuery(nonExistentSlug) against
_handler.HandleAsync(...), and asserts result.IsSuccess is false and
result.Error.StatusCode equals 404 (symbols: _providerRepositoryMock,
GetBySlugAsync, GetPublicProviderByIdOrSlugQuery,
HandleAsync_WhenProviderNotFoundBySlug_ShouldReturnNotFound).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 008b51b3-92ed-4510-a9b5-6727574e0b17

📥 Commits

Reviewing files that changed from the base of the PR and between 848fda8 and a6285c6.

📒 Files selected for processing (4)
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Shared/Utilities/Constants/ApiEndpoints.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Shared/Utilities/Constants/ApiEndpoints.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs

…ostgreSQL/PostGIS, new unit tests for SearchableProvider and API extensions, and a new GetPublicProviderByIdOrSlug query handler with its tests.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs (1)

67-68: ⚠️ Potential issue | 🟠 Major

Inconsistência na regra de privacidade para Services.

Em Line 67-68, a privacidade é forçada por isPrivacyEnabled || !query.IsAuthenticated, mas em Line 76-78 os serviços são controlados só por isPrivacyEnabled. Com usuário anônimo e feature desligada, Services ainda vaza.

🔧 Ajuste sugerido
-        var services = !isPrivacyEnabled
+        var services = !shouldRedactContactInfo
             ? provider.Services.Select(s => s.ServiceName).ToList()
             : new List<string>();

Also applies to: 76-78

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs`
around lines 67 - 68, A regra de privacidade para os Services está
inconsistente: em vez de usar apenas isPrivacyEnabled nas condicionais que
decidem expor Services (linha onde Services são mapeados/filtrados por
isPrivacyEnabled), substitua essa verificação por shouldRedactContactInfo (que
já combina isPrivacyEnabled || !query.IsAuthenticated) para que Services também
sejam ocultados para usuários anônimos; atualize todas as ocorrências que usam
isPrivacyEnabled para controlar Services (referências a Services,
isPrivacyEnabled, query.IsAuthenticated e shouldRedactContactInfo) para usar
shouldRedactContactInfo.
🧹 Nitpick comments (1)
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (1)

39-43: Extrair helper para ativar Provider.Status reduziria duplicação nos testes.

O mesmo bloco de reflection aparece várias vezes. Um helper local deixaria os testes mais curtos e fáceis de manter.

Also applies to: 70-74, 103-106, 140-143, 220-223

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 39 - 43, Extract a small private helper to set Provider.Status via
reflection to remove duplication: implement a method (e.g.,
SetProviderStatus(Provider provider, EProviderStatus status)) inside
GetPublicProviderByIdOrSlugQueryHandlerTests that uses
typeof(Provider).GetProperty(nameof(Provider.Status)), asserts the property is
not null, and calls SetValue(provider, status); then replace the repeated
reflection blocks (the ones referencing Provider.Status and
EProviderStatus.Active at the multiple occurrences) with calls to this helper to
keep tests concise and consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 165-167: The Reconstitute path currently revalidates Slug using
NormalizeAndValidateSlug (which calls SlugHelper.Generate()), while the
migration 20260317151054_AddSlugToSearchableProvider populated slugs with a SQL
expression; to avoid divergence for pathological names, update Reconstitute to
derive/validate slugs using the same C# generator used elsewhere (use
SlugHelper.GenerateWithSuffix() as used by Provider) or explicitly document that
the SQL expression and SlugHelper.Generate() are equivalent; locate the
Reconstitute method and replace the NormalizeAndValidateSlug/SlugHelper.Generate
usage with SlugHelper.GenerateWithSuffix (or align the migration to use the same
generator) so generation/validation logic is consolidated.

In `@src/Modules/Users/Tests/Unit/API/Extensions/APIExtensionsTests.cs`:
- Around line 113-114: The public extension methods AddUsersModule(this
IServiceCollection services, IConfiguration configuration) and
AddUsersModuleWithSchemaIsolationAsync(this IServiceCollection services,
IConfiguration configuration, string? usersRolePassword = null, string?
appRolePassword = null) must explicitly validate null parameters: add
ArgumentNullException.ThrowIfNull(services) and
ArgumentNullException.ThrowIfNull(configuration) at the start of each method,
then update the unit tests (APIExtensionsTests) to expect ArgumentNullException
instead of InvalidOperationException when passing null configuration or
services.

---

Outside diff comments:
In
`@src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs`:
- Around line 67-68: A regra de privacidade para os Services está inconsistente:
em vez de usar apenas isPrivacyEnabled nas condicionais que decidem expor
Services (linha onde Services são mapeados/filtrados por isPrivacyEnabled),
substitua essa verificação por shouldRedactContactInfo (que já combina
isPrivacyEnabled || !query.IsAuthenticated) para que Services também sejam
ocultados para usuários anônimos; atualize todas as ocorrências que usam
isPrivacyEnabled para controlar Services (referências a Services,
isPrivacyEnabled, query.IsAuthenticated e shouldRedactContactInfo) para usar
shouldRedactContactInfo.

---

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 39-43: Extract a small private helper to set Provider.Status via
reflection to remove duplication: implement a method (e.g.,
SetProviderStatus(Provider provider, EProviderStatus status)) inside
GetPublicProviderByIdOrSlugQueryHandlerTests that uses
typeof(Provider).GetProperty(nameof(Provider.Status)), asserts the property is
not null, and calls SetValue(provider, status); then replace the repeated
reflection blocks (the ones referencing Provider.Status and
EProviderStatus.Active at the multiple occurrences) with calls to this helper to
keep tests concise and consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6b7a8067-6b34-4eb5-b97b-9fcc603aaaeb

📥 Commits

Reviewing files that changed from the base of the PR and between 848fda8 and 850f2a6.

📒 Files selected for processing (7)
  • src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/Users/Tests/Unit/API/Extensions/APIExtensionsTests.cs
  • src/Shared/Utilities/Constants/ApiEndpoints.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Modules/SearchProviders/Tests/Integration/SearchProvidersIntegrationTestBase.cs

Comment thread src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs Outdated
Comment thread src/Modules/Users/Tests/Unit/API/Extensions/APIExtensionsTests.cs Outdated
@frigini frigini changed the title Sprint 8c: add provider web app & slug implementation Sprint 8c: slug implementation Mar 18, 2026
Filipe Frigini added 4 commits March 18, 2026 11:03
…nd add `GetPublicProviderByIdOrSlugQueryHandler` with public profile privacy features.
…nd add `GetPublicProviderByIdOrSlugQueryHandler` with public profile privacy features.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (1)

149-167: ⚠️ Potential issue | 🟠 Major

Reconstitute está ignorando o slug persistido.

Na Line 149 você recebe slug, mas na Line 167 ele é descartado e recalculado. Isso pode reconstruir a entidade com um valor diferente do banco.

🔧 Ajuste sugerido
         var searchableProvider = new SearchableProvider(
             new SearchableProviderId(id),
             providerId,
             name,
             location,
             subscriptionTier)
         {
-            Slug = SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]),
+            Slug = NormalizeAndValidateSlug(slug),
             Description = description,
             City = city,
             State = state,
             AverageRating = averageRating,
             TotalReviews = totalReviews,
             ServiceIds = serviceIds,
             IsActive = isActive
         };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 149 - 167, The Reconstitute method is ignoring the incoming slug parameter
and always recalculates it; update the creation of the SearchableProvider so its
Slug property uses the provided slug (parameter slug) when present instead of
unconditionally calling SlugHelper.GenerateWithSuffix. Locate the
Reconstitute/factory code where SearchableProvider is instantiated
(SearchableProvider constructor call) and replace the hard-coded Slug =
SlugHelper.GenerateWithSuffix(...) with logic that assigns Slug = slug (or slug
if not null/empty, otherwise fall back to SlugHelper.GenerateWithSuffix(name,
providerId.ToString("N")[..8])) so persisted slugs are preserved.
🧹 Nitpick comments (4)
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (2)

48-59: Cobertura de testes ainda não valida as novas entradas nulas.

Este bloco cobre bem configuração vazia, mas faltam testes para services == null e configuration == null, que agora são comportamentos explícitos do método.

✅ Sugestão objetiva de testes complementares
+[Fact]
+public void AddUsersModule_WithNullServices_ShouldThrowArgumentNullException()
+{
+    // Arrange
+    IServiceCollection? services = null;
+    var configuration = new ConfigurationBuilder().Build();
+
+    // Act
+    var act = () => Extensions.AddUsersModule(services!, configuration);
+
+    // Assert
+    act.Should().Throw<ArgumentNullException>();
+}
+
+[Fact]
+public void AddUsersModule_WithNullConfiguration_ShouldThrowArgumentNullException()
+{
+    // Arrange
+    var services = new ServiceCollection();
+    IConfiguration? configuration = null;
+
+    // Act
+    var act = () => services.AddUsersModule(configuration!);
+
+    // Assert
+    act.Should().Throw<ArgumentNullException>();
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 48 - 59,
Add two unit tests to ExtensionsTests to cover the new null-argument behaviors:
one that verifies calling ServicesModule extension AddUsersModule(null,
configuration) throws ArgumentNullException for the services parameter, and
another that verifies calling services.AddUsersModule(null) throws
ArgumentNullException for the configuration parameter. Name them e.g.
AddUsersModule_WithNullServices_ShouldThrowArgumentNullException and
AddUsersModule_WithNullConfiguration_ShouldThrowArgumentNullException; use a
ServiceCollection instance and ConfigurationBuilder().Build() for non-null args,
and assert .Should().Throw<ArgumentNullException>() (optionally checking
ParamName) against the AddUsersModule extension method.

66-71: Pequena duplicação de setup de configuração mínima.

Vale extrair um helper para evitar repetição e facilitar manutenção dos testes.

♻️ Refactor opcional
+private static IConfiguration BuildUsersOnlyConfiguration(string connection = "Server=localhost;Database=test;")
+{
+    return new ConfigurationBuilder()
+        .AddInMemoryCollection(new Dictionary<string, string?>
+        {
+            ["ConnectionStrings:Users"] = connection
+        })
+        .Build();
+}
...
- var configuration = new ConfigurationBuilder()
-     .AddInMemoryCollection(new Dictionary<string, string?>
-     {
-         ["ConnectionStrings:Users"] = "Server=localhost;Database=test;"
-     })
-     .Build();
+ var configuration = BuildUsersOnlyConfiguration();

Also applies to: 115-118

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 66 - 71,
Extrai a criação repetida de configuração mínima para um helper reuseable: criar
um método helper (por exemplo CreateMinimalConfiguration or
BuildTestConfiguration) que retorna a IConfiguration/IConfigurationRoot usando
ConfigurationBuilder().AddInMemoryCollection(...) com a chave
"ConnectionStrings:Users" = "Server=localhost;Database=test;". Substituir as
instâncias duplicadas dentro de ExtensionsTests (aos locais que hoje fazem new
ConfigurationBuilder() com AddInMemoryCollection) para chamar esse helper; torne
o helper private static dentro da classe de testes ou num TestBase compartilhado
para evitar duplicação e facilitar manutenção.
src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs (2)

70-70: Alinhe o naming de privacidade para reduzir ambiguidade.

Em Line 70 você passa shouldRedactContactInfo, mas o método recebe isPrivacyEnabled. Renomear o parâmetro para refletir “redact” melhora a leitura e evita interpretação errada da regra.

Also applies to: 106-109

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs`
at line 70, Renomeie o parâmetro booleano passado para os métodos de resolução
de contato para usar o mesmo termo “redact” em vez de “isPrivacyEnabled”: onde o
código chama ResolvePhoneNumbers(shouldRedactContactInfo, businessProfile) e
chamadas similares (ex.: 106-109), atualize a assinatura dos métodos (ex.:
ResolvePhoneNumbers) para aceitar um parâmetro chamado shouldRedactContactInfo
(ou renomeie a variável chamadora para isPrivacyEnabled) de modo consistente,
atualize todas as referências e testes relacionados para refletir o novo nome e
verifique que a lógica interna continue a aplicar a regra de redaction sem
alteração funcional.

50-53: Centralize a mensagem de NotFound para evitar duplicação.

A string "Prestador não encontrado." aparece em dois pontos. Vale extrair para constante/recurso compartilhado para manter consistência e facilitar manutenção/localização.

Based on learnings: prioritize reuse of Shared.Contracts for enums/constants to keep Web aligned with backend/shared code.

Also applies to: 57-60

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs`
around lines 50 - 53, The NotFound message "Prestador não encontrado." is
duplicated in GetPublicProviderByIdOrSlugQueryHandler; extract it into a shared
constant in the Shared.Contracts (e.g., Shared.Contracts.Messages or an existing
Errors enum/class) and replace the inline strings in the handler's
Result<PublicProviderDto?>.Failure(Error.NotFound(...)) calls (both occurrences)
to reference that shared constant (use the constant name when updating the two
Error.NotFound calls in GetPublicProviderByIdOrSlugQueryHandler).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 199-233: The test
HandleAsync_WhenPrivacyFlagIsEnabled_ShouldReturnRestrictedProvider is currently
running with the query default IsAuthenticated = false so the handler hides
contact data regardless of FeatureFlags.PublicProfilePrivacy; update the test to
explicitly set the query as authenticated (set query.IsAuthenticated = true on
the GetPublicProviderByIdOrSlugQuery instance) so the feature flag drives the
behavior, keep the same mocks for
_featureManagerMock.IsEnabledAsync(FeatureFlags.PublicProfilePrivacy) returning
true, and then assert contact fields are hidden; additionally add a new test
(e.g.,
HandleAsync_WhenAuthenticatedAndPrivacyFlagDisabled_ShouldReturnFullContactInfo)
that sets query.IsAuthenticated = true, _featureManagerMock returns false, and
asserts email/phone/services are visible to validate the opposite case.

---

Outside diff comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 149-167: The Reconstitute method is ignoring the incoming slug
parameter and always recalculates it; update the creation of the
SearchableProvider so its Slug property uses the provided slug (parameter slug)
when present instead of unconditionally calling SlugHelper.GenerateWithSuffix.
Locate the Reconstitute/factory code where SearchableProvider is instantiated
(SearchableProvider constructor call) and replace the hard-coded Slug =
SlugHelper.GenerateWithSuffix(...) with logic that assigns Slug = slug (or slug
if not null/empty, otherwise fall back to SlugHelper.GenerateWithSuffix(name,
providerId.ToString("N")[..8])) so persisted slugs are preserved.

---

Nitpick comments:
In
`@src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs`:
- Line 70: Renomeie o parâmetro booleano passado para os métodos de resolução de
contato para usar o mesmo termo “redact” em vez de “isPrivacyEnabled”: onde o
código chama ResolvePhoneNumbers(shouldRedactContactInfo, businessProfile) e
chamadas similares (ex.: 106-109), atualize a assinatura dos métodos (ex.:
ResolvePhoneNumbers) para aceitar um parâmetro chamado shouldRedactContactInfo
(ou renomeie a variável chamadora para isPrivacyEnabled) de modo consistente,
atualize todas as referências e testes relacionados para refletir o novo nome e
verifique que a lógica interna continue a aplicar a regra de redaction sem
alteração funcional.
- Around line 50-53: The NotFound message "Prestador não encontrado." is
duplicated in GetPublicProviderByIdOrSlugQueryHandler; extract it into a shared
constant in the Shared.Contracts (e.g., Shared.Contracts.Messages or an existing
Errors enum/class) and replace the inline strings in the handler's
Result<PublicProviderDto?>.Failure(Error.NotFound(...)) calls (both occurrences)
to reference that shared constant (use the constant name when updating the two
Error.NotFound calls in GetPublicProviderByIdOrSlugQueryHandler).

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 48-59: Add two unit tests to ExtensionsTests to cover the new
null-argument behaviors: one that verifies calling ServicesModule extension
AddUsersModule(null, configuration) throws ArgumentNullException for the
services parameter, and another that verifies calling
services.AddUsersModule(null) throws ArgumentNullException for the configuration
parameter. Name them e.g.
AddUsersModule_WithNullServices_ShouldThrowArgumentNullException and
AddUsersModule_WithNullConfiguration_ShouldThrowArgumentNullException; use a
ServiceCollection instance and ConfigurationBuilder().Build() for non-null args,
and assert .Should().Throw<ArgumentNullException>() (optionally checking
ParamName) against the AddUsersModule extension method.
- Around line 66-71: Extrai a criação repetida de configuração mínima para um
helper reuseable: criar um método helper (por exemplo CreateMinimalConfiguration
or BuildTestConfiguration) que retorna a IConfiguration/IConfigurationRoot
usando ConfigurationBuilder().AddInMemoryCollection(...) com a chave
"ConnectionStrings:Users" = "Server=localhost;Database=test;". Substituir as
instâncias duplicadas dentro de ExtensionsTests (aos locais que hoje fazem new
ConfigurationBuilder() com AddInMemoryCollection) para chamar esse helper; torne
o helper private static dentro da classe de testes ou num TestBase compartilhado
para evitar duplicação e facilitar manutenção.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5f85924c-705b-4d5f-bef3-c1e649317c43

📥 Commits

Reviewing files that changed from the base of the PR and between 850f2a6 and 3658caf.

📒 Files selected for processing (10)
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/ServiceCollectionExtensions.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/ContentSecurityPolicyMiddleware.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/InspectAuthMiddleware.cs
  • src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/Users/API/Extensions.cs
  • src/Modules/Users/Tests/Unit/API/Extensions/APIExtensionsTests.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
  • tests/MeAjudaAi.ApiService.Tests/Unit/Middlewares/InspectAuthMiddlewareTests.cs
💤 Files with no reviewable changes (1)
  • src/Bootstrapper/MeAjudaAi.ApiService/Extensions/ServiceCollectionExtensions.cs
✅ Files skipped from review due to trivial changes (1)
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/InspectAuthMiddleware.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs (1)

45-48: Verificação de duplicação de middlewares necessária.

A alteração evita cabeçalhos duplicados verificando se já existem, o que é correto. Porém, com base na ordem do pipeline (snippets de contexto), ContentSecurityPolicyMiddleware executa antes e já define os mesmos cabeçalhos (X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Referrer-Policy, Permissions-Policy, Content-Security-Policy).

Isso significa que este middleware agora é parcialmente redundante — ele quase nunca adicionará esses cabeçalhos porque já foram definidos anteriormente.

Considere:

  1. Remover os cabeçalhos duplicados de StaticHeaders e manter apenas em um middleware, ou
  2. Documentar explicitamente que este middleware serve como fallback

Além disso, há uma pequena alocação de memória devido ao .Where() LINQ a cada requisição. Uma verificação dentro do loop seria marginalmente mais eficiente:

♻️ Sugestão para evitar alocação do iterador
             // Adiciona cabeçalhos de segurança estáticos eficientemente
-            foreach (var header in StaticHeaders.Where(h => !headers.ContainsKey(h.Key)))
+            foreach (var header in StaticHeaders)
             {
-                headers.Append(header.Key, header.Value);
+                if (!headers.ContainsKey(header.Key))
+                {
+                    headers.Append(header.Key, header.Value);
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`
around lines 45 - 48, O middleware SecurityHeadersMiddleware está redundante
porque ContentSecurityPolicyMiddleware já adiciona os mesmos cabeçalhos listados
em StaticHeaders; remova os cabeçalhos duplicados de StaticHeaders (mantendo-os
apenas em ContentSecurityPolicyMiddleware) ou documente claramente que
SecurityHeadersMiddleware é um fallback e liste apenas cabeçalhos exclusivos;
além disso, evite a alocação do iterador LINQ em cada requisição substituindo
StaticHeaders.Where(...) por um loop simples que verifica
headers.ContainsKey(header.Key) antes de chamar headers.Append — altere o
foreach na classe SecurityHeadersMiddleware para essa verificação direta.
src/Aspire/MeAjudaAi.AppHost/Program.cs (1)

71-73: Supressões de S2068 corretamente aplicadas para credenciais de desenvolvimento.

As supressões #pragma warning disable S2068 estão corretamente delimitadas apenas às linhas com valores padrão hardcoded. A lógica existente já garante que:

  • Em ambientes CI (linhas 65-70 e 108-113), o processo falha se as variáveis de ambiente não estiverem definidas
  • Os valores padrão ("test123", "admin123") são usados apenas em desenvolvimento/testes locais

Como sugestão opcional, considere adicionar um comentário breve explicando o motivo da supressão para facilitar revisões futuras:

 `#pragma` warning disable S2068 // Senha padrão apenas para desenvolvimento local, CI requer variável de ambiente
 testDbPassword = "test123";
 `#pragma` warning restore S2068

,

Also applies to: 114-116, 140-142

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Aspire/MeAjudaAi.AppHost/Program.cs` around lines 71 - 73, Add a one-line
explanatory comment next to each usage of "#pragma warning disable S2068" that
surrounds hardcoded development credentials so reviewers know it's intentional;
specifically, for the assignment to testDbPassword ("test123") and the analogous
assignment(s) to adminPassword ("admin123") and any other places that use the
same pragma, insert a brief comment like "Suppress S2068: intentional hardcoded
dev/test default credential, only used in local/dev scenarios; CI requires env
var" immediately before the pragma and keep the pragma scope unchanged.
src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs (1)

119-119: Uso do operador null-forgiving (!) depende de contrato implícito com o domain service.

O tipo Result<T> possui o atributo [MaybeNull] em Value (conforme src/Contracts/Functional/Result.cs:24-25), indicando que Value pode ser null mesmo quando IsSuccess é true. O operador ! suprime o aviso do compilador mas não oferece proteção em runtime.

Se CreateUserAsync alguma vez retornar Value = null em caso de sucesso:

  • Linha 119 lançaria NullReferenceException
  • A exceção seria capturada, mas linha 130 no bloco catch também lançaria — dessa vez sem tratamento

Considerando que o padrão está sendo adotado consistentemente no PR (conforme AI summary), uma verificação defensiva opcional após o check de IsFailure aumentaria a robustez:

🛡️ Sugestão de verificação defensiva
 if (userResult.IsFailure)
 {
     logger.LogWarning("Failed to register customer {Email}: {Error}", maskedEmail, userResult.Error);
     return Result<UserDto>.Failure(userResult.Error);
 }

+if (userResult.Value is null)
+{
+    logger.LogError("CreateUserAsync returned success but Value is null for {Email}", maskedEmail);
+    return Result<UserDto>.Failure(Error.Internal("Erro inesperado no cadastro. Tente novamente."));
+}
+
+var user = userResult.Value;
+
 try
 {
-    await userRepository.AddAsync(userResult.Value!, cancellationToken);
+    await userRepository.AddAsync(user, cancellationToken);

Depois, substitua todas as ocorrências de userResult.Value! por user.

Also applies to: 130-130, 135-135, 169-171

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs`
at line 119, The code uses the null-forgiving operator on userResult.Value!
(result from CreateUserAsync) which can be [MaybeNull]; add a defensive null
check after verifying userResult.IsFailure: if userResult.Value is null treat it
as a failure (log/return/throw a specific DomainException) and only then assign
var user = userResult.Value and pass user to userRepository.AddAsync and other
calls (replace all userResult.Value! uses with the local user). Also update the
catch block to avoid re-using userResult.Value! and ensure any downstream uses
(e.g., in rollback or logging) reference the validated local user or handle the
null case consistently.
src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs (1)

66-78: Extraia o keycloakId para melhorar reusabilidade e clareza do código.

O padrão Result<T> com verificação IsFailure é comum nesta base de código. Porém, em vez de reutilizar keycloakResult.Value! em linhas 66 e 70, extraia o valor em uma variável local para melhor legibilidade e evitar múltiplas chamadas ao Value!.

🔧 Sugestão de refatoração
+        var keycloakId = keycloakResult.Value!;
         var userResult = User.Create(username, email, firstName, lastName, keycloakResult.Value!, phoneNumber);
+        var userResult = User.Create(username, email, firstName, lastName, keycloakId, phoneNumber);
         if (userResult.IsFailure)
         {
-            var deactivationResult = await keycloakService.DeactivateUserAsync(keycloakResult.Value, CancellationToken.None);
+            var deactivationResult = await keycloakService.DeactivateUserAsync(keycloakId, CancellationToken.None);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs`
around lines 66 - 78, Extraia keycloakResult.Value! para uma variável local (por
exemplo keycloakId) antes de criar o usuário para evitar repetir Value! e
melhorar clareza; use essa keycloakId ao chamar
keycloakService.DeactivateUserAsync(keycloakId, CancellationToken.None), ao
montar a mensagem do logger.LogWarning("Failed to deactivate Keycloak user
{KeycloakId}...", keycloakId, deactivationResult.Error.Message) e ao
retornar/compensar em caso de userResult.IsFailure, mantendo a lógica atual em
KeycloakUserDomainService (variáveis: keycloakResult, userResult; métodos:
DeactivateUserAsync, logger.LogWarning).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Aspire/MeAjudaAi.AppHost/Program.cs`:
- Around line 71-73: Add a one-line explanatory comment next to each usage of
"#pragma warning disable S2068" that surrounds hardcoded development credentials
so reviewers know it's intentional; specifically, for the assignment to
testDbPassword ("test123") and the analogous assignment(s) to adminPassword
("admin123") and any other places that use the same pragma, insert a brief
comment like "Suppress S2068: intentional hardcoded dev/test default credential,
only used in local/dev scenarios; CI requires env var" immediately before the
pragma and keep the pragma scope unchanged.

In
`@src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs`:
- Around line 45-48: O middleware SecurityHeadersMiddleware está redundante
porque ContentSecurityPolicyMiddleware já adiciona os mesmos cabeçalhos listados
em StaticHeaders; remova os cabeçalhos duplicados de StaticHeaders (mantendo-os
apenas em ContentSecurityPolicyMiddleware) ou documente claramente que
SecurityHeadersMiddleware é um fallback e liste apenas cabeçalhos exclusivos;
além disso, evite a alocação do iterador LINQ em cada requisição substituindo
StaticHeaders.Where(...) por um loop simples que verifica
headers.ContainsKey(header.Key) antes de chamar headers.Append — altere o
foreach na classe SecurityHeadersMiddleware para essa verificação direta.

In
`@src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs`:
- Line 119: The code uses the null-forgiving operator on userResult.Value!
(result from CreateUserAsync) which can be [MaybeNull]; add a defensive null
check after verifying userResult.IsFailure: if userResult.Value is null treat it
as a failure (log/return/throw a specific DomainException) and only then assign
var user = userResult.Value and pass user to userRepository.AddAsync and other
calls (replace all userResult.Value! uses with the local user). Also update the
catch block to avoid re-using userResult.Value! and ensure any downstream uses
(e.g., in rollback or logging) reference the validated local user or handle the
null case consistently.

In `@src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs`:
- Around line 66-78: Extraia keycloakResult.Value! para uma variável local (por
exemplo keycloakId) antes de criar o usuário para evitar repetir Value! e
melhorar clareza; use essa keycloakId ao chamar
keycloakService.DeactivateUserAsync(keycloakId, CancellationToken.None), ao
montar a mensagem do logger.LogWarning("Failed to deactivate Keycloak user
{KeycloakId}...", keycloakId, deactivationResult.Error.Message) e ao
retornar/compensar em caso de userResult.IsFailure, mantendo a lógica atual em
KeycloakUserDomainService (variáveis: keycloakResult, userResult; métodos:
DeactivateUserAsync, logger.LogWarning).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 93134376-b215-4408-a6d2-3422dfec58b4

📥 Commits

Reviewing files that changed from the base of the PR and between 3658caf and d7a0a8a.

⛔ Files ignored due to path filters (5)
  • src/Aspire/MeAjudaAi.AppHost/packages.lock.json is excluded by !**/packages.lock.json
  • src/Web/MeAjudaAi.Web.Admin/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.E2E.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Integration.Tests/packages.lock.json is excluded by !**/packages.lock.json
  • tests/MeAjudaAi.Web.Admin.Tests/packages.lock.json is excluded by !**/packages.lock.json
📒 Files selected for processing (15)
  • src/Aspire/MeAjudaAi.AppHost/Program.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs
  • src/Modules/Users/Application/Handlers/Commands/CreateUserCommandHandler.cs
  • src/Modules/Users/Application/Handlers/Commands/DeleteUserCommandHandler.cs
  • src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs
  • src/Modules/Users/Application/Handlers/Commands/UpdateUserProfileCommandHandler.cs
  • src/Modules/Users/Infrastructure/Identity/Keycloak/KeycloakService.cs
  • src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs
  • src/Modules/Users/Infrastructure/Services/LocalDevelopment/LocalDevelopmentUserDomainService.cs
  • src/Modules/Users/Tests/Builders/UserBuilder.cs
  • src/Modules/Users/Tests/Unit/Application/Queries/GetUsersQueryHandlerTests.cs
  • src/Modules/Users/Tests/Unit/Domain/Entities/UserTests.cs
  • src/Shared/Database/DatabaseConstants.cs
  • src/Shared/Messaging/DeadLetter/RabbitMqDeadLetterService.cs
  • tests/MeAjudaAi.Shared.Tests/Unit/Functional/ResultTests.cs
✅ Files skipped from review due to trivial changes (1)
  • src/Modules/Users/Application/Handlers/Commands/DeleteUserCommandHandler.cs

…provider query handlers, a searchable provider entity, and security headers middleware.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (1)

11-14: ⚠️ Potential issue | 🟡 Minor

Inconsistência entre a categoria do trait e a localização do arquivo.

O arquivo está em Tests/Unit/API/ mas o trait na linha 11 indica "Integration". Se são testes de integração, considere mover para uma pasta de integração. Se são testes unitários, atualize o trait para "Unit".

-[Trait("Category", "Integration")]
+[Trait("Category", "Unit")]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 11 - 14, O
arquivo declara a classe ExtensionsTests com o atributo [Trait("Category",
"Integration")] mas está localizado em Tests/Unit/API/, então ajuste para manter
consistência: ou mova a classe ExtensionsTests (e seu arquivo) para a pasta de
testes de integração correspondente, ou altere o atributo [Trait("Category",
"Integration")] para [Trait("Category", "Unit")] (mantendo as demais traits
[Trait("Module", "Users")] e [Trait("Layer", "API")]) para refletir que é um
teste unitário.
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (1)

111-114: ⚠️ Potential issue | 🟡 Minor

Padronize as mensagens de validação para português neste agregado.

As validações de name ainda usam mensagem em inglês ("Provider name cannot be empty."). Se essa exceção escapar para a borda da API, fica inconsistente com a política de idioma do projeto.

Based on learnings: User-facing validation messages and API responses shown to the frontend must be in Portuguese.

Also applies to: 185-188

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 111 - 114, Padronize as mensagens de validação para português no agregado
SearchableProvider: substitua a mensagem em inglês "Provider name cannot be
empty." por uma mensagem em português equivalente (por exemplo "O nome do
provedor não pode ficar vazio.") nas validações que lançam ArgumentException
para o parâmetro name (preserve nameof(name)); aplique a mesma alteração também
na segunda ocorrência mencionada (linhas referentes a 185-188) para manter
consistência em toda a classe SearchableProvider.
♻️ Duplicate comments (1)
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (1)

237-270: ⚠️ Potential issue | 🟡 Minor

O cenário “privacidade desabilitada” ainda não cobre Services.

Em Line 237-270 o teste só valida Email e PhoneNumbers, mas o handler também oculta Services. Se esse campo regredir isoladamente, a suíte continua verde. Inclua um serviço conhecido na etapa de Arrange e valide o conteúdo retornado.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 237 - 270, The test
HandleAsync_WhenAuthenticatedAndPrivacyFlagDisabled_ShouldReturnFullContactInfo
currently asserts Email and PhoneNumbers but misses verifying Services; update
the Arrange to include a known service in the provider's BusinessProfile (use
ProviderBuilder/Create and the BusinessProfile/Services collection) and after
invoking _handler.HandleAsync(new GetPublicProviderByIdOrSlugQuery(...){
IsAuthenticated = true }, ...) add an assertion that result.Value.Services
contains the expected service (e.g., matching by service Id or name) to ensure
Services is returned when FeatureFlags.PublicProfilePrivacy is disabled.
🧹 Nitpick comments (3)
src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs (1)

149-153: Padronize o bloco de compensação para usar user.Id em todos os logs.

Na linha de erro de compensação ainda há dependência de userResult.Value.Id; vale manter consistente com o restante do método usando user.Id.

🔧 Ajuste sugerido
-logger.LogError("Compensation failed for user {UserId}: {Error}", userResult.Value.Id, compensationResult.Error);
+logger.LogError("Compensation failed for user {UserId}: {Error}", user.Id, compensationResult.Error);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs`
around lines 149 - 153, Padronize o log de erro do bloco de compensação para
usar user.Id em vez de userResult.Value.Id; localize a chamada a
userDomainService.DeactivateUserInKeycloakAsync e o if que verifica
compensationResult.IsFailure, e atualizar a chamada
logger.LogError("Compensation failed for user {UserId}: {Error}",
userResult.Value.Id, compensationResult.Error) para passar user.Id como primeiro
argumento, mantendo a mesma mensagem e uso de compensationResult.Error.
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (1)

39-40: Traduza os comentários auxiliares fora do padrão AAA.

Os cabeçalhos Arrange/Act/Assert em inglês estão ok, mas os demais comentários desta suíte também ficaram em inglês. Vale traduzir trechos como “Bypass domain transitions...” e “Fallback to slug lookup...” para manter a convenção do repositório.

Based on learnings: nos testes, apenas os headers Arrange/Act/Assert devem ficar em inglês; comentários e XML documentation devem permanecer em português.

Also applies to: 68-69, 108-109, 124-143, 163-164, 211-212

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 39 - 40, Traduza os comentários auxiliares em inglês nesta suíte de
testes para português mantendo apenas os cabeçalhos Arrange/Act/Assert em
inglês; por exemplo, substitua comentários como "Bypass domain transitions to
set Active status directly for test" e "Fallback to slug lookup..." nos testes
de GetPublicProviderByIdOrSlugQueryHandlerTests (referencie o uso de
SetProviderStatus e os blocos de teste na classe
GetPublicProviderByIdOrSlugQueryHandlerTests) e aplique a mesma tradução aos
trechos indicados (linhas correspondentes aos blocos auxiliares em 68-69,
108-109, 124-143, 163-164, 211-212) para que todos os comentários e documentação
XML fora dos cabeçalhos AAA fiquem em português.
src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs (1)

36-48: Revise a atribuição direta de slug em SearchableProvider para garantir normalização.

O handler normaliza corretamente na leitura (linha 36), e o caminho principal de escrita (entidade Provider) sempre usa SlugHelper.GenerateWithSuffix(), que aplica ToLowerInvariant(). No entanto, SearchableProvider.Create() (linha 167) permite atribuição direta de slug sem normalização se um slug for passado explicitamente. Para evitar inconsistência entre entidades (Provider sempre normaliza; SearchableProvider pode aceitar slug não normalizado), considere adicionar validação ou normalização obrigatória no setter da propriedade Slug ou documentar que slugs devem ser pré-normalizados pelos chamadores. Isso protege contra 404s causados por diferenças de case em ambientes com collation sensível a maiúsculas.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs`
around lines 36 - 48, Atribuição direta de slug em SearchableProvider pode
aceitar valores não normalizados; atualize SearchableProvider.Create (ou o
setter da propriedade Slug) para sempre normalizar o slug usando o mesmo padrão
que Provider (por exemplo, ToLowerInvariant() e quaisquer transformações
aplicadas por SlugHelper.GenerateWithSuffix()), ou forçar validação que rejeite
slugs não-normalizados; garanta que todos os caminhos que constroem
SearchableProvider chamem essa normalização para manter consistência entre
Provider e SearchableProvider e evitar 404s por diferenças de case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Line 167: Ao reconstituir a entidade, não aceite o slug vindo do banco sem
normalização/validação; em vez de atribuir Slug = slug diretamente, primeiro
normalizar e validar (por exemplo usando SlugHelper.Normalize(slug) ou um método
equivalente que faz trim, lowercase e remove/caracteres inválidos) e somente se
o resultado for vazio/inválido gerar o fallback com
SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]); atualize a
atribuição de Slug para usar esse fluxo de normalize → validate → fallback
garantindo que nomes como Slug, SlugHelper.GenerateWithSuffix e
providerId.ToString("N")[..8] sejam usados.

---

Outside diff comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 111-114: Padronize as mensagens de validação para português no
agregado SearchableProvider: substitua a mensagem em inglês "Provider name
cannot be empty." por uma mensagem em português equivalente (por exemplo "O nome
do provedor não pode ficar vazio.") nas validações que lançam ArgumentException
para o parâmetro name (preserve nameof(name)); aplique a mesma alteração também
na segunda ocorrência mencionada (linhas referentes a 185-188) para manter
consistência em toda a classe SearchableProvider.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 11-14: O arquivo declara a classe ExtensionsTests com o atributo
[Trait("Category", "Integration")] mas está localizado em Tests/Unit/API/, então
ajuste para manter consistência: ou mova a classe ExtensionsTests (e seu
arquivo) para a pasta de testes de integração correspondente, ou altere o
atributo [Trait("Category", "Integration")] para [Trait("Category", "Unit")]
(mantendo as demais traits [Trait("Module", "Users")] e [Trait("Layer", "API")])
para refletir que é um teste unitário.

---

Duplicate comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 237-270: The test
HandleAsync_WhenAuthenticatedAndPrivacyFlagDisabled_ShouldReturnFullContactInfo
currently asserts Email and PhoneNumbers but misses verifying Services; update
the Arrange to include a known service in the provider's BusinessProfile (use
ProviderBuilder/Create and the BusinessProfile/Services collection) and after
invoking _handler.HandleAsync(new GetPublicProviderByIdOrSlugQuery(...){
IsAuthenticated = true }, ...) add an assertion that result.Value.Services
contains the expected service (e.g., matching by service Id or name) to ensure
Services is returned when FeatureFlags.PublicProfilePrivacy is disabled.

---

Nitpick comments:
In
`@src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs`:
- Around line 36-48: Atribuição direta de slug em SearchableProvider pode
aceitar valores não normalizados; atualize SearchableProvider.Create (ou o
setter da propriedade Slug) para sempre normalizar o slug usando o mesmo padrão
que Provider (por exemplo, ToLowerInvariant() e quaisquer transformações
aplicadas por SlugHelper.GenerateWithSuffix()), ou forçar validação que rejeite
slugs não-normalizados; garanta que todos os caminhos que constroem
SearchableProvider chamem essa normalização para manter consistência entre
Provider e SearchableProvider e evitar 404s por diferenças de case.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 39-40: Traduza os comentários auxiliares em inglês nesta suíte de
testes para português mantendo apenas os cabeçalhos Arrange/Act/Assert em
inglês; por exemplo, substitua comentários como "Bypass domain transitions to
set Active status directly for test" e "Fallback to slug lookup..." nos testes
de GetPublicProviderByIdOrSlugQueryHandlerTests (referencie o uso de
SetProviderStatus e os blocos de teste na classe
GetPublicProviderByIdOrSlugQueryHandlerTests) e aplique a mesma tradução aos
trechos indicados (linhas correspondentes aos blocos auxiliares em 68-69,
108-109, 124-143, 163-164, 211-212) para que todos os comentários e documentação
XML fora dos cabeçalhos AAA fiquem em português.

In
`@src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs`:
- Around line 149-153: Padronize o log de erro do bloco de compensação para usar
user.Id em vez de userResult.Value.Id; localize a chamada a
userDomainService.DeactivateUserInKeycloakAsync e o if que verifica
compensationResult.IsFailure, e atualizar a chamada
logger.LogError("Compensation failed for user {UserId}: {Error}",
userResult.Value.Id, compensationResult.Error) para passar user.Id como primeiro
argumento, mantendo a mesma mensagem e uso de compensationResult.Error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 14bb993e-16c0-4a9a-b8b8-738fd2154fc5

📥 Commits

Reviewing files that changed from the base of the PR and between d7a0a8a and 6647285.

📒 Files selected for processing (10)
  • src/Aspire/MeAjudaAi.AppHost/Program.cs
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs
  • src/Modules/Providers/Application/Handlers/Queries/GetProviderByIdQueryHandler.cs
  • src/Modules/Providers/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandler.cs
  • src/Modules/Providers/Domain/Constants/ProviderErrors.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs
  • src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/SecurityHeadersMiddleware.cs
  • src/Modules/Users/Infrastructure/Services/KeycloakUserDomainService.cs
  • src/Aspire/MeAjudaAi.AppHost/Program.cs

Comment thread src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs Outdated
Filipe Frigini added 2 commits March 18, 2026 13:47
…roduce SearchableProvider entity, and implement RegisterCustomerCommandHandler with related API extension tests.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (2)

7-11: Inconsistência entre documentação XML e trait de categoria.

A documentação XML na linha 8 ainda menciona "Testes de integração", mas o trait na linha 11 foi alterado para "Unit". Considere atualizar a documentação para manter consistência.

📝 Correção sugerida
 /// <summary>
-/// Testes de integração dos métodos de extensão do módulo Users
-/// Foca em cenários de integração e configuração completa
+/// Testes unitários dos métodos de extensão do módulo Users
+/// Foca em cenários de configuração e registro de serviços
 /// </summary>
 [Trait("Category", "Unit")]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 7 - 11, The
XML documentation summary on the ExtensionsTests.cs file currently says "Testes
de integração dos métodos de extensão do módulo Users" but the test is marked
with [Trait("Category", "Unit")]; update the XML <summary> to reflect unit tests
(e.g., "Testes unitários dos métodos de extensão do módulo Users") so the
docstring and the [Trait("Category", "Unit")] attribute are consistent; locate
the XML comment above the test class or file header and change the phrase
"integração" to "unitários".

90-105: Considere também limpar DOTNET_ENVIRONMENT para evitar falhas intermitentes.

O método IsSecurityBypassEnvironment() (conforme context snippet 3) verifica tanto DOTNET_ENVIRONMENT quanto ASPNETCORE_ENVIRONMENT. Se o ambiente de CI/CD definir DOTNET_ENVIRONMENT como "Development" ou "Testing", o teste pode falhar inesperadamente, pois a condição de bypass será satisfeita mesmo com ASPNETCORE_ENVIRONMENT configurado como "Production".

Além disso, a manipulação de variáveis de ambiente globais pode causar condições de corrida se os testes executarem em paralelo.

🛡️ Correção sugerida para maior robustez
         var originalEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
+        var originalDotnetEnv = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
         try
         {
             // Força ambiente não teste/dev para testar o fallback de exception
             Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Production");
+            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Production");
 
             // Act & Assert
             var act = () => services.AddUsersModule(configuration);
             
             act.Should().Throw<InvalidOperationException>()
                .WithMessage("Connection for Users module not configured");
         }
         finally
         {
             Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", originalEnv);
+            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", originalDotnetEnv);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 90 - 105,
The test relies on IsSecurityBypassEnvironment() which reads both
DOTNET_ENVIRONMENT and ASPNETCORE_ENVIRONMENT, so update the test to also save
and restore the original DOTNET_ENVIRONMENT, and clear or set it to a non-bypass
value before asserting the exception: capture
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") into a local variable,
call Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", null) or
"Production" before Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT",
"Production"), run the act/assert against
services.AddUsersModule(configuration), and restore both environment variables
in the finally block; additionally consider marking the test to prevent parallel
execution if environment mutation could race with other tests.
src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs (1)

160-168: Evite exceção como fluxo normal no Reconstitute.

Aqui o catch pode virar caminho frequente para dados legados; vale simplificar com checagem explícita e fallback sem exceção.

♻️ Proposta de ajuste
-        string normalizedSlug;
-        try
-        {
-            normalizedSlug = NormalizeAndValidateSlug(slug);
-        }
-        catch (ArgumentException)
-        {
-            normalizedSlug = SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]);
-        }
+        var normalizedSlug = SlugHelper.Generate(slug ?? string.Empty);
+        if (string.IsNullOrWhiteSpace(normalizedSlug))
+        {
+            normalizedSlug = SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]);
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs` around
lines 160 - 168, No use exceções como fluxo normal em Reconstitute: remova o
try/catch que chama NormalizeAndValidateSlug(slug) e, em vez disso,
implementar/ver usar uma versão não-throwing (por exemplo
TryNormalizeSlug(string slug, out string normalized) ou um IsValidSlug check)
para validar e normalizar antes do fallback; se TryNormalizeSlug retornar true
use o normalized, caso contrário gere o slug de fallback com
SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]).
Certifique-se de atualizar/usar o método NormalizeAndValidateSlug ou adicionar
TryNormalizeSlug para evitar lançar ArgumentException no caminho normal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 185-187: O mock de _providerRepositoryMock está configurado com o
tipo errado: o método GetByIdAsync espera um ProviderId, mas o setup usa
providerId (Guid), então a configuração nunca é atingida; atualize o Setup para
usar It.Is<ProviderId>(id => id.Value == providerId) ou construa explicitamente
new ProviderId(providerId) no argumento do Setup para que GetByIdAsync(new
ProviderId(...), It.IsAny<CancellationToken>()) corresponda corretamente e o
teste valide o caminho esperado rather than dependendo do comportamento padrão
do Moq.

---

Nitpick comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 160-168: No use exceções como fluxo normal em Reconstitute: remova
o try/catch que chama NormalizeAndValidateSlug(slug) e, em vez disso,
implementar/ver usar uma versão não-throwing (por exemplo
TryNormalizeSlug(string slug, out string normalized) ou um IsValidSlug check)
para validar e normalizar antes do fallback; se TryNormalizeSlug retornar true
use o normalized, caso contrário gere o slug de fallback com
SlugHelper.GenerateWithSuffix(name, providerId.ToString("N")[..8]).
Certifique-se de atualizar/usar o método NormalizeAndValidateSlug ou adicionar
TryNormalizeSlug para evitar lançar ArgumentException no caminho normal.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 7-11: The XML documentation summary on the ExtensionsTests.cs file
currently says "Testes de integração dos métodos de extensão do módulo Users"
but the test is marked with [Trait("Category", "Unit")]; update the XML
<summary> to reflect unit tests (e.g., "Testes unitários dos métodos de extensão
do módulo Users") so the docstring and the [Trait("Category", "Unit")] attribute
are consistent; locate the XML comment above the test class or file header and
change the phrase "integração" to "unitários".
- Around line 90-105: The test relies on IsSecurityBypassEnvironment() which
reads both DOTNET_ENVIRONMENT and ASPNETCORE_ENVIRONMENT, so update the test to
also save and restore the original DOTNET_ENVIRONMENT, and clear or set it to a
non-bypass value before asserting the exception: capture
Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") into a local variable,
call Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", null) or
"Production" before Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT",
"Production"), run the act/assert against
services.AddUsersModule(configuration), and restore both environment variables
in the finally block; additionally consider marking the test to prevent parallel
execution if environment mutation could race with other tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c5255bbe-e1b6-4a72-9e7a-414179b582a2

📥 Commits

Reviewing files that changed from the base of the PR and between 6647285 and b3337f1.

📒 Files selected for processing (4)
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Modules/Users/Application/Handlers/Commands/RegisterCustomerCommandHandler.cs

Filipe Frigini added 2 commits March 18, 2026 14:00
…er retrieval by ID or slug with privacy controls.
…ring active/inactive providers, ID/slug queries, and privacy feature flag scenarios.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (2)

68-69: Padronize os comentários não-AAA para português.

Os blocos de comentário nesses trechos estão em inglês; para manter consistência do repositório, vale migrar para português e manter apenas os headers AAA em inglês.

Based on learnings: "Adopt a language policy across the codebase: comments/XML summaries em português" e "In unit test code, keep AAA headers (Arrange, Act, Assert) in English."

Also applies to: 98-99, 133-134

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 68 - 69, Substitua os comentários explicativos não-AAA pelo
equivalente em português no bloco que precede a chamada
SetProviderStatus(provider, EProviderStatus.Active) — mantenha apenas os headers
AAA (Arrange/Act/Assert) em inglês — e aplique a mesma padronização nos outros
blocos de comentário mencionados (os dois blocos adicionais indicados pelo
revisor).

122-155: O teste de fallback não comprova explicitamente o caminho ID → slug.

O cenário em Line 122-155 valida o resultado final, mas não garante que o handler tentou GetByIdAsync e depois fez fallback para GetBySlugAsync. Vale fortalecer com Verify (e, se quiser, MockSequence) para evitar falso-positivo em refactors futuros.

💡 Ajuste sugerido
-        _providerRepositoryMock
-            .Setup(x => x.GetByIdAsync(It.IsAny<ProviderId>(), It.IsAny<CancellationToken>()))
+        _providerRepositoryMock
+            .Setup(x => x.GetByIdAsync(It.Is<ProviderId>(id => id.Value == slugGuid), It.IsAny<CancellationToken>()))
             .ReturnsAsync((Provider?)null);

         // Fallback para a busca por slug retorna o provedor
         _providerRepositoryMock
             .Setup(x => x.GetBySlugAsync(slugValue, It.IsAny<CancellationToken>()))
             .ReturnsAsync(provider);
@@
         result.IsSuccess.Should().BeTrue();
         result.Value.Should().NotBeNull();
         result.Value!.Id.Should().Be(provider.Id);
+
+        _providerRepositoryMock.Verify(
+            x => x.GetByIdAsync(It.Is<ProviderId>(id => id.Value == slugGuid), It.IsAny<CancellationToken>()),
+            Times.Once);
+        _providerRepositoryMock.Verify(
+            x => x.GetBySlugAsync(slugValue, It.IsAny<CancellationToken>()),
+            Times.Once);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 122 - 155, In the test
HandleAsync_WhenSlugIsValidGuidString_ShouldFallbackToSlugLookup, explicitly
verify that the handler attempted an ID lookup then fell back to a slug lookup
by calling Verify on _providerRepositoryMock: assert GetByIdAsync was invoked
once with a ProviderId corresponding to slugGuid (or matching its Value) and
that GetBySlugAsync was invoked once with slugValue; optionally use MockSequence
to enforce call order. This ensures the test checks the ID→slug path rather than
only the final result.
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (1)

8-10: Ajustar o XML summary para refletir foco unitário.

Na Line 9, “cenários de integração” conflita com [Trait("Category", "Unit")]. Vale alinhar a descrição para evitar ambiguidade na documentação do teste.

💡 Diff sugerido
 /// <summary>
 /// Testes unitários dos métodos de extensão do módulo Users
-/// Foca em cenários de integração e configuração completa
+/// Foca em cenários unitários de configuração e contrato do módulo
 /// </summary>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 8 - 10,
Ajuste o XML <summary> do arquivo ExtensionsTests.cs para refletir que estes são
testes unitários: remova ou substitua a frase “cenários de integração” por algo
como “cenários unitários” ou “cenários focados em unidade” para alinhar com
[Trait("Category", "Unit")] e evitar ambiguidade; busque o comentário XML acima
da classe/método ExtensionsTests e atualize a descrição para indicar claramente
foco unitário.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs`:
- Around line 284-289: Atualize a mensagem de erro lançada em
NormalizeAndValidateSlug para cobrir tanto valores vazios quanto formatos
inválidos: quando TryNormalizeSlug(slug, out var normalized) retornar falso
lance uma ArgumentException com texto em Português indicando que o identificador
do provedor não pode estar vazio nem em formato inválido (por exemplo: "O
identificador do provedor não pode estar vazio nem em formato inválido."),
mantendo o parâmetro nomeado como nameof(slug) e sem alterar a lógica de
validação existente.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Line 14: The test class uses [Collection("NonParallel Environment Tests")] but
no matching CollectionDefinition exists; create a new public class named
NonParallelEnvironmentTestsCollection annotated with
[CollectionDefinition("NonParallel Environment Tests", DisableParallelization =
true)] (for example in src/Modules/Users/Tests/GlobalTestConfiguration.cs) so
tests that modify ASPNETCORE_ENVIRONMENT and DOTNET_ENVIRONMENT run with
parallelization disabled.

---

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 68-69: Substitua os comentários explicativos não-AAA pelo
equivalente em português no bloco que precede a chamada
SetProviderStatus(provider, EProviderStatus.Active) — mantenha apenas os headers
AAA (Arrange/Act/Assert) em inglês — e aplique a mesma padronização nos outros
blocos de comentário mencionados (os dois blocos adicionais indicados pelo
revisor).
- Around line 122-155: In the test
HandleAsync_WhenSlugIsValidGuidString_ShouldFallbackToSlugLookup, explicitly
verify that the handler attempted an ID lookup then fell back to a slug lookup
by calling Verify on _providerRepositoryMock: assert GetByIdAsync was invoked
once with a ProviderId corresponding to slugGuid (or matching its Value) and
that GetBySlugAsync was invoked once with slugValue; optionally use MockSequence
to enforce call order. This ensures the test checks the ID→slug path rather than
only the final result.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 8-10: Ajuste o XML <summary> do arquivo ExtensionsTests.cs para
refletir que estes são testes unitários: remova ou substitua a frase “cenários
de integração” por algo como “cenários unitários” ou “cenários focados em
unidade” para alinhar com [Trait("Category", "Unit")] e evitar ambiguidade;
busque o comentário XML acima da classe/método ExtensionsTests e atualize a
descrição para indicar claramente foco unitário.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1cb64b0b-e8a3-40c8-8a94-5dd39ed8f515

📥 Commits

Reviewing files that changed from the base of the PR and between b3337f1 and 0dea388.

⛔ Files ignored due to path filters (5)
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.cs is excluded by !**/Migrations/**
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/ProvidersDbContextModelSnapshot.cs is excluded by !**/Migrations/**
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.cs is excluded by !**/Migrations/**
📒 Files selected for processing (3)
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs

Comment thread src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
…tests for public provider retrieval, including privacy feature handling.
@frigini
Copy link
Copy Markdown
Owner Author

frigini commented Mar 18, 2026

@coderabbitai full review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs (1)

80-148: LGTM!

Excelente cobertura de testes para a validação de slug:

  • Testes de rejeição para slugs vazios e com espaços em branco
  • Testes de normalização para lowercase
  • Mensagens de erro em português validadas corretamente nas assertions

Considere adicionar um teste para slugs compostos apenas por caracteres especiais (ex: "!!!"), que também devem ser rejeitados após normalização resultar em string vazia.

💡 Teste sugerido
[Fact]
public void Create_WithSpecialCharactersOnlySlug_ShouldThrowArgumentException()
{
    // Arrange
    var providerId = Guid.NewGuid();
    var location = new GeoPoint(-23.5505, -46.6333);

    // Act
    var act = () => SearchableProvider.Create(providerId, "Valid Name", "!!!", location);

    // Assert
    act.Should().Throw<ArgumentException>()
        .WithMessage("*O identificador do provedor não pode estar vazio nem em formato inválido*");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`
around lines 80 - 148, Add a unit test that ensures slugs made only of special
characters are rejected: create a new test similar to Create_WithEmptySlug but
call SearchableProvider.Create(providerId, "Valid Name", "!!!", location) and
assert it throws ArgumentException with the same message pattern ("*O
identificador do provedor não pode estar vazio nem em formato inválido*"); this
verifies SearchableProvider.Create (and by extension normalization logic used by
UpdateBasicInfo) fails when normalization yields an empty slug.
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (3)

214-215: Padronize este comentário inline para português.

Os cabeçalhos AAA podem ficar em inglês, mas esse comentário ficou fora da convenção do repositório. Algo como // Ignora transições de domínio para definir o status Active diretamente no teste já resolve. Based on learnings: In all C# files, keep logging messages in English, but ensure comments and XML documentation (///

, /// , etc.) are written in Portuguese across the codebase. In unit test code (e.g., under any .../Tests/…/*.cs), keep AAA headers in tests (Arrange, Act, Assert) in English and do not translate them.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 214 - 215, Altere o comentário inline que precede a chamada
SetProviderStatus(provider, EProviderStatus.Active) para português, por exemplo:
"// Ignora transições de domínio para definir o status Active diretamente no
teste"; mantenha os cabeçalhos AAA (Arrange, Act, Assert) em inglês nos testes e
preserve mensagens de log em inglês, enquanto comentários e documentação XML
(///) no código C# devem estar em português.

71-77: Os testes ainda não exercitam o Trim() da normalização do slug.

O setup espera provider.Slug.Trim().ToLowerInvariant(), mas as queries passam o slug sem espaços extras (provider.Slug / ToUpperInvariant()). Se o handler perder o Trim(), essa suíte continua verde. Vale adicionar um caso com espaços nas pontas, por exemplo " {provider.Slug.ToUpperInvariant()} ".

Also applies to: 101-109

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 71 - 77, The test currently sets up _providerRepositoryMock to
expect normalizedSlug (provider.Slug.Trim().ToLowerInvariant()) but calls new
GetPublicProviderByIdOrSlugQuery(provider.Slug), so Trim() isn't exercised; add
a test variant that passes a slug with leading/trailing spaces and different
casing (e.g. $"  {provider.Slug.ToUpperInvariant()}  ") to
GetPublicProviderByIdOrSlugQuery and update the mock setup to expect the
handler's normalized value (call Trim() and ToLowerInvariant() on that dirty
input) so the test will fail if the handler omits Trim() or case normalization
(reference normalizedSlug, _providerRepositoryMock.Setup and
GetPublicProviderByIdOrSlugQuery).

279-284: Adicione WithStatus(...) ou AsActive() ao ProviderBuilder para abstrair a reflexão.

O ProviderBuilder não expõe métodos para configurar Provider.Status. O helper SetProviderStatus que usa reflexão é necessário atualmente, mas essa abordagem acopla os testes ao detalhe de implementação. Adicione um método de fluência (como WithStatus() ou AsActive()) ao builder, similar aos existentes (WithTier(), AsIndividual(), etc.), para abstrair essa configuração e remover a necessidade do helper em 6 pontos de teste diferentes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 279 - 284, Add a fluent setter on ProviderBuilder (e.g.,
WithStatus(EProviderStatus status) and/or AsActive()) to configure
Provider.Status instead of using the reflection helper SetProviderStatus;
implement the builder method to store the chosen status and apply it when
building the Provider (mirror existing patterns used by WithTier/AsIndividual),
then update the six tests to call the new builder method and remove usages of
SetProviderStatus and the reflection helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 214-215: Altere o comentário inline que precede a chamada
SetProviderStatus(provider, EProviderStatus.Active) para português, por exemplo:
"// Ignora transições de domínio para definir o status Active diretamente no
teste"; mantenha os cabeçalhos AAA (Arrange, Act, Assert) em inglês nos testes e
preserve mensagens de log em inglês, enquanto comentários e documentação XML
(///) no código C# devem estar em português.
- Around line 71-77: The test currently sets up _providerRepositoryMock to
expect normalizedSlug (provider.Slug.Trim().ToLowerInvariant()) but calls new
GetPublicProviderByIdOrSlugQuery(provider.Slug), so Trim() isn't exercised; add
a test variant that passes a slug with leading/trailing spaces and different
casing (e.g. $"  {provider.Slug.ToUpperInvariant()}  ") to
GetPublicProviderByIdOrSlugQuery and update the mock setup to expect the
handler's normalized value (call Trim() and ToLowerInvariant() on that dirty
input) so the test will fail if the handler omits Trim() or case normalization
(reference normalizedSlug, _providerRepositoryMock.Setup and
GetPublicProviderByIdOrSlugQuery).
- Around line 279-284: Add a fluent setter on ProviderBuilder (e.g.,
WithStatus(EProviderStatus status) and/or AsActive()) to configure
Provider.Status instead of using the reflection helper SetProviderStatus;
implement the builder method to store the chosen status and apply it when
building the Provider (mirror existing patterns used by WithTier/AsIndividual),
then update the six tests to call the new builder method and remove usages of
SetProviderStatus and the reflection helper.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`:
- Around line 80-148: Add a unit test that ensures slugs made only of special
characters are rejected: create a new test similar to Create_WithEmptySlug but
call SearchableProvider.Create(providerId, "Valid Name", "!!!", location) and
assert it throws ArgumentException with the same message pattern ("*O
identificador do provedor não pode estar vazio nem em formato inválido*"); this
verifies SearchableProvider.Create (and by extension normalization logic used by
UpdateBasicInfo) fails when normalization yields an empty slug.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f9528bd1-f45c-4dba-a784-17dc8a77a11c

📥 Commits

Reviewing files that changed from the base of the PR and between b3337f1 and 0830eaa.

⛔ Files ignored due to path filters (5)
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.cs is excluded by !**/Migrations/**
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/ProvidersDbContextModelSnapshot.cs is excluded by !**/Migrations/**
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.cs is excluded by !**/Migrations/**
📒 Files selected for processing (4)
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs

…lugQueryHandler and SearchableProvider entity mapping
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
src/Modules/Providers/Tests/Builders/ProviderBuilder.cs (1)

84-93: Considere extrair um helper para evitar duplicação de reflexão (Tier e Status).

O bloco novo repete o mesmo fluxo de GetProperty + throw + SetValue já usado para Tier; centralizar isso reduz risco de divergência futura.

♻️ Refatoração sugerida
                 // Define tier se especificado
                 if (_tier.HasValue)
                 {
-                    var prop = typeof(Provider).GetProperty(nameof(Provider.Tier));
-                    if (prop == null)
-                    {
-                        throw new InvalidOperationException($"Property '{nameof(Provider.Tier)}' was not found on class {nameof(Provider)}.");
-                    }
-                    prop.SetValue(provider, _tier.Value);
+                    SetPropertyOrThrow(provider, nameof(Provider.Tier), _tier.Value);
                 }

                 // Define status se especificado
                 if (_status.HasValue)
                 {
-                    var prop = typeof(Provider).GetProperty(nameof(Provider.Status));
-                    if (prop == null)
-                    {
-                        throw new InvalidOperationException($"Property '{nameof(Provider.Status)}' was not found on class {nameof(Provider)}.");
-                    }
-                    prop.SetValue(provider, _status.Value);
+                    SetPropertyOrThrow(provider, nameof(Provider.Status), _status.Value);
                 }
+    private static void SetPropertyOrThrow<T>(Provider provider, string propertyName, T value)
+    {
+        var prop = typeof(Provider).GetProperty(propertyName);
+        if (prop == null)
+        {
+            throw new InvalidOperationException($"Property '{propertyName}' was not found on class {nameof(Provider)}.");
+        }
+
+        prop.SetValue(provider, value);
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Providers/Tests/Builders/ProviderBuilder.cs` around lines 84 -
93, The reflection logic for setting Provider.Status duplicates the flow used
for Provider.Tier; extract a private helper on ProviderBuilder (e.g.,
SetProviderProperty(provider, string propertyName, object value) or
SetPropertyValue) that does typeof(Provider).GetProperty(propertyName), throws
the same InvalidOperationException if null, and calls SetValue, then replace the
direct reflection blocks in the Tier and Status branches to call that helper
with nameof(Provider.Tier)/nameof(Provider.Status) and the corresponding value;
ensure the helper signature and exception text match the existing style so
behavior remains identical.
src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs (2)

23-28: Defina um padrão explícito para a feature flag no setup comum.

Hoje vários testes dependem de retorno implícito do mock para IsEnabledAsync. Deixar PublicProfilePrivacy = false explícito no construtor melhora determinismo e legibilidade da suíte.

♻️ Ajuste sugerido
 public GetPublicProviderByIdOrSlugQueryHandlerTests()
 {
     _providerRepositoryMock = new Mock<IProviderRepository>();
     _featureManagerMock = new Mock<IFeatureManager>();
+    _featureManagerMock
+        .Setup(x => x.IsEnabledAsync(FeatureFlags.PublicProfilePrivacy))
+        .ReturnsAsync(false);
     _handler = new GetPublicProviderByIdOrSlugQueryHandler(_providerRepositoryMock.Object, _featureManagerMock.Object);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 23 - 28, No construtor de
GetPublicProviderByIdOrSlugQueryHandlerTests deixe explícito o comportamento da
feature flag: configure o mock _featureManagerMock para retornar false quando
IsEnabledAsync for chamado para a flag "PublicProfilePrivacy" (por exemplo
usando Setup/SetupSequence para
_featureManagerMock.IsEnabledAsync("PublicProfilePrivacy", ...) retornando
Task.FromResult(false)); isso garante que todos os testes partam de um padrão
determinístico e legível ao invés de depender do retorno implícito do mock.

187-227: Cobertura negativa por slug ainda está incompleta.

Os cenários negativos (inativo e não encontrado) só exercitam consulta por ID. Vale adicionar equivalentes por slug para evitar regressão específica desse caminho.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`
around lines 187 - 227, Add equivalent negative tests that exercise the slug
path in GetPublicProviderByIdOrSlugQueryHandlerTests: create two new facts named
HandleAsync_WhenProviderIsNotActive_BySlug_ShouldReturnNotFound and
HandleAsync_WhenProviderNotFound_BySlug_ShouldReturnNotFound that mirror the
existing ID-based tests but construct GetPublicProviderByIdOrSlugQuery with the
provider slug string, set up the repository mock to respond to the slug lookup
(e.g., Setup(x => x.GetBySlugAsync(provider.Slug,
It.IsAny<CancellationToken>())) to return the inactive provider or null
respectively), invoke _handler.HandleAsync(query, CancellationToken.None) and
assert result.IsSuccess is false and result.Error.StatusCode is 404. Ensure you
use ProviderBuilder to provide a slug for the first test and a random slug for
the not-found test so the slug-specific code path is covered.
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (1)

103-104: Evite acoplar este teste ao texto literal de uma exceção da infraestrutura.

Em src/Modules/Users/API/Extensions.cs:17-29, AddUsersModule só faz os guards de null e delega para AddInfrastructure(configuration). Validar aqui a mensagem exata transforma este teste do módulo em um teste do detalhe interno da infraestrutura; qualquer ajuste de texto lá quebra a suíte sem alterar o contrato público deste método.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 103 - 104,
O teste está acoplado ao texto literal da exceção da infraestrutura; abra
ExtensionsTests.cs, localize a asserção que usa
act.Should().Throw<InvalidOperationException>().WithMessage("Connection for
Users module not configured") e remova a verificação de mensagem mantendo apenas
a asserção de tipo (act.Should().Throw<InvalidOperationException>()), pois
AddUsersModule delega para AddInfrastructure e o contrato público do módulo deve
ser testado pelo tipo da exceção, não pelo texto exato.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 229-263: The test
HandleAsync_WhenPrivacyFlagIsEnabled_ShouldReturnRestrictedProvider currently
asserts result.Value.Services.Should().BeEmpty() but never adds any services to
the arranged provider, so the assertion can pass for the wrong reason; update
the Arrange to add at least one service to the provider (via
ProviderBuilder.WithServices(...) or provider.AddService(...) / equivalent on
the ProviderBuilder used) before calling _providerRepositoryMock.Setup(...),
keep the feature flag setup (_featureManagerMock.IsEnabledAsync(...)) and the
query (GetPublicProviderByIdOrSlugQuery) the same, then assert after Act that
Services is empty to verify privacy masking actually removed the service(s).

---

Nitpick comments:
In `@src/Modules/Providers/Tests/Builders/ProviderBuilder.cs`:
- Around line 84-93: The reflection logic for setting Provider.Status duplicates
the flow used for Provider.Tier; extract a private helper on ProviderBuilder
(e.g., SetProviderProperty(provider, string propertyName, object value) or
SetPropertyValue) that does typeof(Provider).GetProperty(propertyName), throws
the same InvalidOperationException if null, and calls SetValue, then replace the
direct reflection blocks in the Tier and Status branches to call that helper
with nameof(Provider.Tier)/nameof(Provider.Status) and the corresponding value;
ensure the helper signature and exception text match the existing style so
behavior remains identical.

In
`@src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs`:
- Around line 23-28: No construtor de
GetPublicProviderByIdOrSlugQueryHandlerTests deixe explícito o comportamento da
feature flag: configure o mock _featureManagerMock para retornar false quando
IsEnabledAsync for chamado para a flag "PublicProfilePrivacy" (por exemplo
usando Setup/SetupSequence para
_featureManagerMock.IsEnabledAsync("PublicProfilePrivacy", ...) retornando
Task.FromResult(false)); isso garante que todos os testes partam de um padrão
determinístico e legível ao invés de depender do retorno implícito do mock.
- Around line 187-227: Add equivalent negative tests that exercise the slug path
in GetPublicProviderByIdOrSlugQueryHandlerTests: create two new facts named
HandleAsync_WhenProviderIsNotActive_BySlug_ShouldReturnNotFound and
HandleAsync_WhenProviderNotFound_BySlug_ShouldReturnNotFound that mirror the
existing ID-based tests but construct GetPublicProviderByIdOrSlugQuery with the
provider slug string, set up the repository mock to respond to the slug lookup
(e.g., Setup(x => x.GetBySlugAsync(provider.Slug,
It.IsAny<CancellationToken>())) to return the inactive provider or null
respectively), invoke _handler.HandleAsync(query, CancellationToken.None) and
assert result.IsSuccess is false and result.Error.StatusCode is 404. Ensure you
use ProviderBuilder to provide a slug for the first test and a random slug for
the not-found test so the slug-specific code path is covered.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 103-104: O teste está acoplado ao texto literal da exceção da
infraestrutura; abra ExtensionsTests.cs, localize a asserção que usa
act.Should().Throw<InvalidOperationException>().WithMessage("Connection for
Users module not configured") e remova a verificação de mensagem mantendo apenas
a asserção de tipo (act.Should().Throw<InvalidOperationException>()), pois
AddUsersModule delega para AddInfrastructure e o contrato público do módulo deve
ser testado pelo tipo da exceção, não pelo texto exato.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e19aa021-bc9a-4575-9f97-99e388d31b32

📥 Commits

Reviewing files that changed from the base of the PR and between b3337f1 and 6dc9cb0.

⛔ Files ignored due to path filters (5)
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.cs is excluded by !**/Migrations/**
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/ProvidersDbContextModelSnapshot.cs is excluded by !**/Migrations/**
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.cs is excluded by !**/Migrations/**
📒 Files selected for processing (5)
  • src/Modules/Providers/Tests/Builders/ProviderBuilder.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs

…roviders modules, including cross-module workflows.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
src/Modules/Providers/Tests/Builders/ProviderBuilder.cs (1)

227-236: Boa consolidação da lógica de reflection.

O helper SetPropertyValue centraliza o código de reflection, facilitando manutenção e evitando duplicação. A validação da existência da propriedade com exceção clara é apropriada para infraestrutura de testes.

Opcionalmente, para maior robustez, você poderia validar se a propriedade possui um setter antes de chamar SetValue:

♻️ Sugestão opcional de validação adicional
 private static void SetPropertyValue(Provider provider, string propertyName, object value)
 {
-    var prop = typeof(Provider).GetProperty(propertyName);
+    var prop = typeof(Provider).GetProperty(propertyName)
+        ?? throw new InvalidOperationException($"Property '{propertyName}' was not found on class {nameof(Provider)}.");
+    
-    if (prop == null)
-    {
-        throw new InvalidOperationException($"Property '{propertyName}' was not found on class {nameof(Provider)}.");
-    }
+    if (!prop.CanWrite)
+    {
+        throw new InvalidOperationException($"Property '{propertyName}' on class {nameof(Provider)} does not have a setter.");
+    }
+    
     prop.SetValue(provider, value);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Providers/Tests/Builders/ProviderBuilder.cs` around lines 227 -
236, The SetPropertyValue helper currently assumes the discovered PropertyInfo
is writable; update SetPropertyValue to verify the property has a setter (e.g.,
check prop.CanWrite or prop.GetSetMethod(true) != null) before calling
prop.SetValue and throw a clear InvalidOperationException if the property is
read-only on Provider, so callers get a descriptive error instead of a runtime
exception from SetValue.
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (1)

20-23: Padronize a chave de connection string para evitar cobertura ambígua

No mesmo arquivo, alguns cenários usam ConnectionStrings:Users (Line 22) e outros usam ConnectionStrings:DefaultConnection (ex.: Line 61/134/183). Vale unificar para reduzir falso positivo de cobertura em caminhos diferentes de configuração.

💡 Sugestão de ajuste
     return new ConfigurationBuilder()
         .AddInMemoryCollection(new Dictionary<string, string?>
         {
             ["ConnectionStrings:Users"] = "Server=localhost;Database=test;"
+            ,["ConnectionStrings:DefaultConnection"] = "Server=localhost;Database=test;"
         })
         .Build();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 20 - 23,
Padronize a chave da connection string nos testes para evitar caminhos de
configuração diferentes: substitua todas as ocorrências divergentes
("ConnectionStrings:Users" e "ConnectionStrings:DefaultConnection") por uma
única chave consistente — por exemplo "ConnectionStrings:DefaultConnection" — em
todo o arquivo ExtensionsTests.cs e nos outros cenários de teste que referenciam
essas chaves; busque pelas strings "ConnectionStrings:Users" e
"ConnectionStrings:DefaultConnection" e atualize os valores no
AddInMemoryCollection e nos asserts/consumos correspondentes para usar apenas a
chave escolhida.
src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs (1)

17-49: Adicione assert explícito de Slug nos happy paths principais.

Os testes já passam slug, mas não verificam explicitamente o valor final em Create_WithValidData e UpdateBasicInfo_WithValidData.

💡 Diff sugerido
@@
         provider.ProviderId.Should().Be(providerId);
         provider.Name.Should().Be(name);
+        provider.Slug.Should().Be("test-provider");
         provider.Location.Should().Be(location);
@@
         provider.UpdateBasicInfo(newName, "updated-provider", newDescription, newCity, newState);
@@
         provider.Name.Should().Be(newName);
+        provider.Slug.Should().Be("updated-provider");
         provider.Description.Should().Be(newDescription);

Also applies to: 166-184

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`
around lines 17 - 49, The tests call SearchableProvider.Create and
UpdateBasicInfo but don't assert the Slug value; add explicit assertions for the
Slug property in Create_WithValidData_ShouldCreateSearchableProvider and
UpdateBasicInfo_WithValidData to validate the final slug value (e.g., assert
provider.Slug equals the expected "test-provider" after Create, and assert it
equals the updated slug after calling UpdateBasicInfo), referencing the
SearchableProvider.Create and UpdateBasicInfo flows to locate where to add the
checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Modules/Providers/Tests/Builders/ProviderBuilder.cs`:
- Around line 227-236: The SetPropertyValue helper currently assumes the
discovered PropertyInfo is writable; update SetPropertyValue to verify the
property has a setter (e.g., check prop.CanWrite or prop.GetSetMethod(true) !=
null) before calling prop.SetValue and throw a clear InvalidOperationException
if the property is read-only on Provider, so callers get a descriptive error
instead of a runtime exception from SetValue.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`:
- Around line 17-49: The tests call SearchableProvider.Create and
UpdateBasicInfo but don't assert the Slug value; add explicit assertions for the
Slug property in Create_WithValidData_ShouldCreateSearchableProvider and
UpdateBasicInfo_WithValidData to validate the final slug value (e.g., assert
provider.Slug equals the expected "test-provider" after Create, and assert it
equals the updated slug after calling UpdateBasicInfo), referencing the
SearchableProvider.Create and UpdateBasicInfo flows to locate where to add the
checks.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 20-23: Padronize a chave da connection string nos testes para
evitar caminhos de configuração diferentes: substitua todas as ocorrências
divergentes ("ConnectionStrings:Users" e "ConnectionStrings:DefaultConnection")
por uma única chave consistente — por exemplo
"ConnectionStrings:DefaultConnection" — em todo o arquivo ExtensionsTests.cs e
nos outros cenários de teste que referenciam essas chaves; busque pelas strings
"ConnectionStrings:Users" e "ConnectionStrings:DefaultConnection" e atualize os
valores no AddInMemoryCollection e nos asserts/consumos correspondentes para
usar apenas a chave escolhida.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 96022a60-d772-4440-a3b3-2c6436d78f03

📥 Commits

Reviewing files that changed from the base of the PR and between b3337f1 and 5abf621.

⛔ Files ignored due to path filters (5)
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/20260318164815_AddProviderSlug.cs is excluded by !**/Migrations/**
  • src/Modules/Providers/Infrastructure/Persistence/Migrations/ProvidersDbContextModelSnapshot.cs is excluded by !**/Migrations/**
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.Designer.cs is excluded by !**/Migrations/**, !**/*.Designer.cs
  • src/Modules/SearchProviders/Infrastructure/Persistence/Migrations/20260318164839_AddSearchableProviderSlug.cs is excluded by !**/Migrations/**
📒 Files selected for processing (7)
  • src/Modules/Providers/Tests/Builders/ProviderBuilder.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs
  • src/Modules/SearchProviders/Domain/Entities/SearchableProvider.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
  • tests/MeAjudaAi.E2E.Tests/CrossModule/ProviderServiceCatalogSearchWorkflowTests.cs
  • tests/MeAjudaAi.E2E.Tests/Modules/SearchProviders/SearchProvidersEndToEndTests.cs
✅ Files skipped from review due to trivial changes (3)
  • tests/MeAjudaAi.E2E.Tests/CrossModule/ProviderServiceCatalogSearchWorkflowTests.cs
  • tests/MeAjudaAi.E2E.Tests/Modules/SearchProviders/SearchProvidersEndToEndTests.cs
  • src/Modules/Providers/Tests/Unit/Application/Handlers/Queries/GetPublicProviderByIdOrSlugQueryHandlerTests.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs (1)

96-108: Inconsistência na asserção da mensagem de erro.

O teste Create_WithEmptySlug_ShouldThrowArgumentException (linhas 92-93) valida a mensagem de erro específica, mas este teste para slug com espaços em branco não faz a mesma validação. Para garantir consistência e verificar que o caminho de validação correto está sendo exercitado, considere adicionar a mesma asserção de mensagem.

♻️ Sugestão para adicionar asserção da mensagem
         // Assert
-        act.Should().Throw<ArgumentException>();
+        act.Should().Throw<ArgumentException>()
+            .WithMessage("*O identificador do provedor não pode estar vazio nem em formato inválido*");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`
around lines 96 - 108, The test
Create_WithWhitespaceSlug_ShouldThrowArgumentException currently only asserts an
ArgumentException is thrown; update it to also assert the exception message
matches the same expected message used in
Create_WithEmptySlug_ShouldThrowArgumentException by chaining the message
assertion on the thrown exception from SearchableProvider.Create (i.e., assert
the exception's Message equals the same expected error string used in the other
test to ensure consistency of validation).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Line 14: The current [Collection("NonParallel Environment Tests")] only
serializes tests within this assembly and does not prevent races across
assemblies when tests mutate ASPNETCORE_ENVIRONMENT/DOTNET_ENVIRONMENT; fix by
either disabling assembly-level parallelization for the affected job by adding
an assembly-wide setting (e.g., add [assembly:
CollectionBehavior(DisableTestParallelization = true)] to AssemblyInfo.cs or set
"parallelizeAssembly": false in xunit.runner.json) or eliminate global env var
mutation in the ExtensionsTests class (remove/replace code that sets environment
variables) so tests no longer rely on process-wide state.

---

Nitpick comments:
In
`@src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs`:
- Around line 96-108: The test
Create_WithWhitespaceSlug_ShouldThrowArgumentException currently only asserts an
ArgumentException is thrown; update it to also assert the exception message
matches the same expected message used in
Create_WithEmptySlug_ShouldThrowArgumentException by chaining the message
assertion on the thrown exception from SearchableProvider.Create (i.e., assert
the exception's Message equals the same expected error string used in the other
test to ensure consistency of validation).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0cda1ac8-236f-4492-9177-c13bc404d029

📥 Commits

Reviewing files that changed from the base of the PR and between 5abf621 and 85406d7.

📒 Files selected for processing (3)
  • src/Modules/Providers/Tests/Builders/ProviderBuilder.cs
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Modules/Providers/Tests/Builders/ProviderBuilder.cs

Comment thread src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (1)

53-82: Uso inconsistente do helper BuildTestConfiguration().

Alguns testes usam o helper (linhas 32, 102, 145) enquanto outros criam configurações inline mais completas. Isso é aceitável quando as configurações adicionais (Keycloak, etc.) são necessárias para o cenário específico do teste, mas considere documentar ou nomear os métodos de forma a deixar claro quando configuração completa vs. mínima é necessária.

Also applies to: 111-138, 185-212

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 53 - 82,
The test AddUsersModule_ShouldAddApplicationAndInfrastructureServices (and other
tests at lines referenced) inconsistently creates configuration inline instead
of using the shared BuildTestConfiguration() helper; standardize by either
updating BuildTestConfiguration() to support full vs minimal configurations
(e.g., add parameters or an overload to include Keycloak and ConnectionStrings)
and replace the inline configuration in this test with a call to the appropriate
helper variant, or clearly document/rename the helper to
BuildTestConfiguration_Minimal and add BuildTestConfiguration_Full then update
tests to call the correct helper so intent (minimal vs full config) is explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 53-82: The test
AddUsersModule_ShouldAddApplicationAndInfrastructureServices (and other tests at
lines referenced) inconsistently creates configuration inline instead of using
the shared BuildTestConfiguration() helper; standardize by either updating
BuildTestConfiguration() to support full vs minimal configurations (e.g., add
parameters or an overload to include Keycloak and ConnectionStrings) and replace
the inline configuration in this test with a call to the appropriate helper
variant, or clearly document/rename the helper to BuildTestConfiguration_Minimal
and add BuildTestConfiguration_Full then update tests to call the correct helper
so intent (minimal vs full config) is explicit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 24d64353-7be7-4b06-bba5-39949667a4af

📥 Commits

Reviewing files that changed from the base of the PR and between 85406d7 and a49c3dc.

📒 Files selected for processing (2)
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Modules/SearchProviders/Tests/Unit/Domain/Entities/SearchableProviderTests.cs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs (1)

98-111: ⚠️ Potential issue | 🟠 Major

Isole também DOTNET_ENVIRONMENT para evitar flakiness.

Nas Line 98-111, o teste força apenas ASPNETCORE_ENVIRONMENT = "Production". Se DOTNET_ENVIRONMENT estiver definido para um ambiente de bypass, o resultado pode variar por ambiente/runner. Recomendo setar/restaurar os dois valores no try/finally.

🔧 Ajuste sugerido
-        var originalEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
+        var originalAspnetEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
+        var originalDotnetEnvironment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
         try
         {
             Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Production");
+            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Production");

             // Act & Assert
             var act = () => services.AddUsersModule(configuration);
             
             act.Should().Throw<InvalidOperationException>();
         }
         finally
         {
-            Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", originalEnvironment);
+            Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", originalAspnetEnvironment);
+            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", originalDotnetEnvironment);
         }
#!/bin/bash
set -euo pipefail

echo "1) Localizar helper de ambiente e verificar quais variáveis ele considera"
fd EnvironmentHelpers.cs src tests
rg -n -C3 'IsSecurityBypassEnvironment|ASPNETCORE_ENVIRONMENT|DOTNET_ENVIRONMENT' src tests --type cs

echo
echo "2) Encontrar outros testes que mutam variáveis de ambiente globais"
rg -n -C2 'Environment\.SetEnvironmentVariable\("ASPNETCORE_ENVIRONMENT"|Environment\.SetEnvironmentVariable\("DOTNET_ENVIRONMENT"' src tests --type cs
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs` around lines 98 - 111,
The test currently only sets ASPNETCORE_ENVIRONMENT to "Production", which can
be bypassed if DOTNET_ENVIRONMENT is set; update the test around the
Environment.SetEnvironmentVariable calls (the block that captures
originalEnvironment, sets ASPNETCORE_ENVIRONMENT and calls
services.AddUsersModule/asserts act.Should().Throw<InvalidOperationException>())
to also capture the original DOTNET_ENVIRONMENT, set DOTNET_ENVIRONMENT =
"Production" inside the try, and restore DOTNET_ENVIRONMENT in the finally
alongside restoring originalEnvironment so both environment variables are
consistently isolated for the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs`:
- Around line 98-111: The test currently only sets ASPNETCORE_ENVIRONMENT to
"Production", which can be bypassed if DOTNET_ENVIRONMENT is set; update the
test around the Environment.SetEnvironmentVariable calls (the block that
captures originalEnvironment, sets ASPNETCORE_ENVIRONMENT and calls
services.AddUsersModule/asserts act.Should().Throw<InvalidOperationException>())
to also capture the original DOTNET_ENVIRONMENT, set DOTNET_ENVIRONMENT =
"Production" inside the try, and restore DOTNET_ENVIRONMENT in the finally
alongside restoring originalEnvironment so both environment variables are
consistently isolated for the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a3bf20e2-0de2-425a-bc2e-4d3939a2ed23

📥 Commits

Reviewing files that changed from the base of the PR and between a49c3dc and f5199f2.

📒 Files selected for processing (1)
  • src/Modules/Users/Tests/Unit/API/ExtensionsTests.cs

@github-actions
Copy link
Copy Markdown

📊 Code Coverage Report

Coverage: 90.36% (extracted from Cobertura (Aggregated Direct))

📈 Coverage Details

  • Coverage badges: Coverage
  • Minimum threshold: 80% (warning) / 90% (good)
  • Report format: Auto-detected from OpenCover/Cobertura XML files
  • Coverage source: Cobertura (Aggregated Direct)

📋 Coverage Analysis

  • Line Coverage: Shows percentage of code lines executed during tests
  • Branch Coverage: Shows percentage of code branches/conditions tested
  • Complexity: Code complexity metrics for maintainability

🎯 Quality Gates

  • Pass: Coverage ≥ 90%
  • ⚠️ Warning: Coverage 80-89%
  • Fail: Coverage < 80%

📁 Artifacts

  • Coverage reports: Available in workflow artifacts
  • Test results: TRX files with detailed test execution data

This comment is updated automatically on each push to track coverage trends.

@frigini frigini merged commit d59ada3 into master Mar 19, 2026
9 checks passed
@frigini frigini deleted the feature/sprint-8c-provider-web branch March 19, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant